import asyncio

from app.models.document import IngestRequest
from app.models.parse_run import DocParseRun
from app.workers import ingestion_worker


def _request():
    return IngestRequest(repo_id="ad/repo", doc_path="README.md", content="# Intro")


def test_run_ingestion_job_skips_when_claim_not_acquired(monkeypatch):
    run = DocParseRun(repo_id="ad/repo", doc_path="README.md")
    called = {"process": 0}

    async def _get_run(_run_id):
        return run

    async def _claim_run(_run_id, stale_after_seconds):
        assert stale_after_seconds > 0
        return False

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

    monkeypatch.setattr(ingestion_worker.parse_run_repo, "get_run_by_id", _get_run)
    monkeypatch.setattr(ingestion_worker.parse_run_repo, "claim_run_for_processing", _claim_run)
    monkeypatch.setattr(ingestion_worker.ingestion_service, "process_ingestion", _process)

    asyncio.run(ingestion_worker.run_ingestion_job("run_1", _request()))

    assert called["process"] == 0


def test_run_ingestion_job_retries_transient_failures(monkeypatch):
    run = DocParseRun(repo_id="ad/repo", doc_path="README.md")
    called = {"process": 0, "retrying": 0, "sleep": []}

    async def _get_run(_run_id):
        return run

    async def _claim_run(_run_id, stale_after_seconds):
        return True

    async def _mark_retrying(run_id, retry_count, error, next_retry_at):
        assert run_id == "run_1"
        called["retrying"] += 1
        assert retry_count == 1
        assert "timeout" in error
        assert next_retry_at is not None

    async def _process(_run_id, _request):
        called["process"] += 1
        if called["process"] == 1:
            raise TimeoutError("timeout")

    async def _sleep(seconds):
        called["sleep"].append(seconds)

    monkeypatch.setattr(ingestion_worker, "env_int", lambda *_args, **_kwargs: 1)
    monkeypatch.setattr(ingestion_worker, "env_float", lambda *_args, **_kwargs: 0.0)

    monkeypatch.setattr(ingestion_worker.parse_run_repo, "get_run_by_id", _get_run)
    monkeypatch.setattr(ingestion_worker.parse_run_repo, "claim_run_for_processing", _claim_run)
    monkeypatch.setattr(ingestion_worker.parse_run_repo, "mark_run_retrying", _mark_retrying)
    monkeypatch.setattr(ingestion_worker.ingestion_service, "process_ingestion", _process)
    monkeypatch.setattr(ingestion_worker.asyncio, "sleep", _sleep)

    asyncio.run(ingestion_worker.run_ingestion_job("run_1", _request()))

    assert called["process"] == 2
    assert called["retrying"] == 1
    assert called["sleep"] == [0.0]


def test_run_ingestion_job_raises_on_non_retryable_failure(monkeypatch):
    run = DocParseRun(repo_id="ad/repo", doc_path="README.md")

    async def _get_run(_run_id):
        return run

    async def _claim_run(_run_id, stale_after_seconds):
        return True

    async def _process(_run_id, _request):
        raise ValueError("invalid markdown")

    monkeypatch.setattr(ingestion_worker.parse_run_repo, "get_run_by_id", _get_run)
    monkeypatch.setattr(ingestion_worker.parse_run_repo, "claim_run_for_processing", _claim_run)
    monkeypatch.setattr(ingestion_worker.ingestion_service, "process_ingestion", _process)

    try:
        asyncio.run(ingestion_worker.run_ingestion_job("run_1", _request()))
        assert False
    except ValueError:
        assert True
