from __future__ import annotations

import httpx
import pytest

from firecrawl_client.client import FirecrawlClient
from firecrawl_client.exceptions import FirecrawlError, FirecrawlTimeout
from firecrawl_client.models import ExtractResult


def _make_client(transport: httpx.MockTransport, **kwargs) -> FirecrawlClient:
    return FirecrawlClient(
        api_url="https://crawl.example.test",
        api_key="fc-test",
        transport=transport,
        **kwargs,
    )


def test_extract_polls_until_completed(monkeypatch):
    """First call submits job, then polling sees pending → completed."""
    sleep_calls: list[float] = []
    monkeypatch.setattr("time.sleep", lambda s: sleep_calls.append(s))

    sequence = iter([
        # Initial POST /v2/extract — returns job id
        httpx.Response(200, json={"success": True, "id": "job-123"}),
        # GET /v2/extract/job-123 — pending
        httpx.Response(200, json={"success": True, "status": "processing"}),
        # GET /v2/extract/job-123 — completed
        httpx.Response(200, json={
            "success": True,
            "status": "completed",
            "data": {"contacts": [{"name": "Anna", "email": "anna@roma.com"}]},
            "sources": ["https://roma.com/team"],
        }),
    ])

    def handler(_request: httpx.Request) -> httpx.Response:
        return next(sequence)

    transport = httpx.MockTransport(handler)
    client = _make_client(transport)
    result = client.extract(
        ["https://roma.com/team"],
        schema={"type": "object", "properties": {"contacts": {"type": "array"}}},
    )

    assert isinstance(result, ExtractResult)
    assert result.data["contacts"][0]["email"] == "anna@roma.com"
    assert result.sources == ["https://roma.com/team"]
    assert len(sleep_calls) >= 1


def test_extract_raises_timeout_when_polling_exceeds_max(monkeypatch):
    """If status never becomes completed within poll_timeout, raise FirecrawlTimeout."""
    fake_time = {"now": 0.0}

    def fake_monotonic() -> float:
        return fake_time["now"]

    def fake_sleep(s: float) -> None:
        fake_time["now"] += s + 100  # advance dramatically each sleep

    monkeypatch.setattr("time.monotonic", fake_monotonic)
    monkeypatch.setattr("time.sleep", fake_sleep)

    def handler(request: httpx.Request) -> httpx.Response:
        if request.method == "POST":
            return httpx.Response(200, json={"success": True, "id": "job-x"})
        return httpx.Response(200, json={"success": True, "status": "processing"})

    transport = httpx.MockTransport(handler)
    client = _make_client(transport)
    with pytest.raises(FirecrawlTimeout):
        client.extract(
            ["https://roma.com"],
            schema={"type": "object"},
            poll_timeout=5.0,
        )


def test_extract_failed_status_raises_error(monkeypatch):
    monkeypatch.setattr("time.sleep", lambda _: None)

    sequence = iter([
        httpx.Response(200, json={"success": True, "id": "job-y"}),
        httpx.Response(200, json={"success": True, "status": "failed", "error": "no data"}),
    ])

    def handler(_request: httpx.Request) -> httpx.Response:
        return next(sequence)

    transport = httpx.MockTransport(handler)
    client = _make_client(transport)
    with pytest.raises(FirecrawlError):
        client.extract(["https://roma.com"], schema={"type": "object"})
