import asyncio
from pathlib import Path

from app.models.events import FileChangedEvent
from app.services import stream_ingestion_service


def _event(**overrides):
    payload = {
        "event_id": "evt_snap_x_file_deadbeef",
        "event_version": "v1",
        "created_at": "2026-05-27T00:00:00Z",
        "repo_id": "ad/leisure-ui.com",
        "snapshot_id": "snap_0123456789abcdef",
        "commit_sha": "abc123",
        "file_path": "README.md",
        "language": "markdown",
        "file_kind": "docs",
        "change_type": "added",
        "artifact_uri": None,
        "old_path": None,
    }
    payload.update(overrides)
    return FileChangedEvent.model_validate(payload)


def test_build_stream_key_defaults_to_files_changed(monkeypatch):
    monkeypatch.delenv("REDIS_STREAM_PREFIX", raising=False)
    monkeypatch.delenv("REDIS_STREAM_NAME", raising=False)

    assert stream_ingestion_service.build_stream_key() == "files.changed"


def test_build_stream_key_uses_prefix(monkeypatch):
    monkeypatch.setenv("REDIS_STREAM_PREFIX", "dev")
    monkeypatch.setenv("REDIS_STREAM_NAME", "files.changed")

    assert stream_ingestion_service.build_stream_key() == "dev.files.changed"


def test_repo_workspace_root_uses_repo_sync_naming(monkeypatch, tmp_path):
    monkeypatch.setenv("GIT_WORKSPACE_PATH", str(tmp_path))
    event = _event(repo_id="ad/leisure-ui.com")

    root = stream_ingestion_service._repo_workspace_root(event)

    assert root == (tmp_path / "ad__leisure-ui.com").resolve()


def test_process_file_changed_event_skips_non_markdown_files(monkeypatch):
    event = _event(file_kind="code", file_path="main.go")

    called = {"upsert": 0, "process": 0}

    async def _get_completed_run(_dedupe_key):
        return None

    async def _upsert_run_for_event(_run):
        called["upsert"] += 1
        return _run

    async def _process_ingestion(_run_id, _request, from_stream=False):
        called["process"] += 1

    monkeypatch.setattr(stream_ingestion_service.parse_run_repo, "get_completed_run", _get_completed_run)
    monkeypatch.setattr(stream_ingestion_service.parse_run_repo, "upsert_run_for_event", _upsert_run_for_event)
    monkeypatch.setattr(stream_ingestion_service.ingestion_service, "process_ingestion", _process_ingestion)

    outcome = asyncio.run(stream_ingestion_service.process_file_changed_event(event))

    assert outcome == "skipped"
    assert called == {"upsert": 0, "process": 0}


def test_process_file_changed_event_skips_unsupported_doc_patterns(monkeypatch):
    event = _event(file_kind="docs", file_path="guides/setup.md")

    called = {"upsert": 0, "process": 0}

    async def _get_completed_run(_dedupe_key):
        return None

    async def _upsert_run_for_event(_run):
        called["upsert"] += 1
        return _run

    async def _process_ingestion(_run_id, _request, from_stream=False):
        called["process"] += 1

    monkeypatch.setattr(stream_ingestion_service.parse_run_repo, "get_completed_run", _get_completed_run)
    monkeypatch.setattr(stream_ingestion_service.parse_run_repo, "upsert_run_for_event", _upsert_run_for_event)
    monkeypatch.setattr(stream_ingestion_service.ingestion_service, "process_ingestion", _process_ingestion)

    outcome = asyncio.run(stream_ingestion_service.process_file_changed_event(event))

    assert outcome == "skipped"
    assert called == {"upsert": 0, "process": 0}


def test_process_file_changed_event_processes_markdown_docs(monkeypatch, tmp_path):
    monkeypatch.setenv("GIT_WORKSPACE_PATH", str(tmp_path))

    repo_root = tmp_path / "ad__leisure-ui.com"
    file_path = repo_root / "README.md"
    file_path.parent.mkdir(parents=True, exist_ok=True)
    file_path.write_text("# Intro\nBody", encoding="utf-8")

    event = _event(file_kind="docs", file_path="README.md")

    called = {"upsert": 0, "process": 0}

    class _Run:
        run_id = "run_1"

    async def _get_completed_run(_dedupe_key):
        return None

    async def _upsert_run_for_event(_run):
        called["upsert"] += 1
        return _Run()

    async def _process_ingestion(_run_id, _request, from_stream=False):
        called["process"] += 1
        assert from_stream is True

    monkeypatch.setattr(stream_ingestion_service.parse_run_repo, "get_completed_run", _get_completed_run)
    monkeypatch.setattr(stream_ingestion_service.parse_run_repo, "upsert_run_for_event", _upsert_run_for_event)
    monkeypatch.setattr(stream_ingestion_service.ingestion_service, "process_ingestion", _process_ingestion)

    outcome = asyncio.run(stream_ingestion_service.process_file_changed_event(event))

    assert outcome == "processed"
    assert called == {"upsert": 1, "process": 1}


def test_process_file_changed_event_skips_empty_markdown_docs(monkeypatch, tmp_path):
    monkeypatch.setenv("GIT_WORKSPACE_PATH", str(tmp_path))

    repo_root = tmp_path / "ad__leisure-ui.com"
    file_path = repo_root / "README.md"
    file_path.parent.mkdir(parents=True, exist_ok=True)
    file_path.write_text("   ", encoding="utf-8")

    event = _event(file_kind="docs", file_path="README.md")

    called = {"upsert": 0, "process": 0}

    async def _get_completed_run(_dedupe_key):
        return None

    async def _upsert_run_for_event(_run):
        called["upsert"] += 1
        return _run

    async def _process_ingestion(_run_id, _request, from_stream=False):
        called["process"] += 1

    monkeypatch.setattr(stream_ingestion_service.parse_run_repo, "get_completed_run", _get_completed_run)
    monkeypatch.setattr(stream_ingestion_service.parse_run_repo, "upsert_run_for_event", _upsert_run_for_event)
    monkeypatch.setattr(stream_ingestion_service.ingestion_service, "process_ingestion", _process_ingestion)

    outcome = asyncio.run(stream_ingestion_service.process_file_changed_event(event))

    assert outcome == "skipped"
    assert called == {"upsert": 0, "process": 0}


def test_process_file_changed_event_skips_completed_dedupe(monkeypatch):
    event = _event(file_kind="docs", file_path="README.md")

    class _Run:
        run_id = "run_existing"

    async def _get_completed_run(_dedupe_key):
        return _Run()

    async def _upsert_run_for_event(_run):
        raise AssertionError("should not upsert completed event")

    monkeypatch.setattr(stream_ingestion_service.parse_run_repo, "get_completed_run", _get_completed_run)
    monkeypatch.setattr(stream_ingestion_service.parse_run_repo, "upsert_run_for_event", _upsert_run_for_event)

    outcome = asyncio.run(stream_ingestion_service.process_file_changed_event(event))

    assert outcome == "skipped"
