from __future__ import annotations

import json

import httpx
import pytest

from firecrawl_client.client import FirecrawlClient
from firecrawl_client.exceptions import (
    FirecrawlBadRequest,
    FirecrawlServerError,
)
from firecrawl_client.models import ScrapeResult


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


def test_scrape_posts_v2_endpoint_with_correct_body():
    captured: dict = {}

    def handler(request: httpx.Request) -> httpx.Response:
        captured["url"] = str(request.url)
        captured["method"] = request.method
        captured["headers"] = dict(request.headers)
        captured["body"] = json.loads(request.content)
        return httpx.Response(200, json={
            "success": True,
            "data": {"markdown": "# Hi", "metadata": {"title": "Test"}},
        })

    transport = httpx.MockTransport(handler)
    client = _make_client(transport)
    result = client.scrape("https://roma.com/team")

    assert captured["method"] == "POST"
    assert captured["url"] == "https://crawl.example.test/v2/scrape"
    assert captured["headers"]["authorization"] == "Bearer fc-test"
    assert captured["body"]["url"] == "https://roma.com/team"
    assert captured["body"]["formats"] == ["markdown"]
    assert captured["body"]["onlyMainContent"] is True
    assert captured["body"]["blockAds"] is True
    assert captured["body"]["removeBase64Images"] is True
    assert captured["body"]["maxAge"] == 86_400_000
    assert captured["body"]["timeout"] == 60_000

    assert isinstance(result, ScrapeResult)
    assert result.markdown == "# Hi"
    assert result.metadata == {"title": "Test"}
    assert result.status_code == 200


def test_scrape_4xx_raises_bad_request_no_retry():
    """4xx → FirecrawlBadRequest immediately, no retry."""
    call_count = {"n": 0}

    def handler(_request: httpx.Request) -> httpx.Response:
        call_count["n"] += 1
        return httpx.Response(400, json={"success": False, "error": "bad url"})

    transport = httpx.MockTransport(handler)
    client = _make_client(transport)
    with pytest.raises(FirecrawlBadRequest):
        client.scrape("https://roma.com/team")
    assert call_count["n"] == 1


def test_scrape_5xx_retries_then_succeeds():
    """5xx, 5xx, 200 → 3 calls, success returned."""
    sequence = iter([
        httpx.Response(500, json={"error": "boom1"}),
        httpx.Response(500, json={"error": "boom2"}),
        httpx.Response(200, json={
            "success": True,
            "data": {"markdown": "# Recovered", "metadata": {}},
        }),
    ])

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

    transport = httpx.MockTransport(handler)
    client = _make_client(transport)
    result = client.scrape("https://roma.com/team")
    assert result.markdown == "# Recovered"


def test_scrape_5xx_exhausts_retries():
    def handler(_request: httpx.Request) -> httpx.Response:
        return httpx.Response(503, json={"error": "down"})

    transport = httpx.MockTransport(handler)
    client = _make_client(transport)
    with pytest.raises(FirecrawlServerError):
        client.scrape("https://roma.com/team")


def test_scrape_uses_custom_max_age():
    captured: dict = {}

    def handler(request: httpx.Request) -> httpx.Response:
        captured["body"] = json.loads(request.content)
        return httpx.Response(200, json={
            "success": True,
            "data": {"markdown": "x", "metadata": {}},
        })

    transport = httpx.MockTransport(handler)
    client = _make_client(transport)
    client.scrape("https://roma.com", max_age_ms=1000)
    assert captured["body"]["maxAge"] == 1000
