from __future__ import annotations

from unittest.mock import MagicMock

import httpx

from email_enrichment.team_scraper import scrape_team_pages


def test_scrape_extracts_emails_from_team_page(monkeypatch):
    monkeypatch.delenv("FIRECRAWL_API_URL", raising=False)
    monkeypatch.delenv("FIRECRAWL_API_KEY", raising=False)

    html = """
    <html><body>
      <h2>Our Team</h2>
      <p>Jane Doe — CEO — <a href="mailto:jane@acme.com">jane@acme.com</a></p>
      <p>Contact: hello@acme.com</p>
      <p>Off-domain: other@example.com</p>
    </body></html>
    """

    def handler(request: httpx.Request) -> httpx.Response:
        if request.url.path == "/team":
            return httpx.Response(
                200, text=html, headers={"Content-Type": "text/html"}
            )
        return httpx.Response(404)

    with httpx.Client(transport=httpx.MockTransport(handler)) as client:
        result = scrape_team_pages("acme.com", ["/team", "/about"], client=client)

    assert set(result.emails) == {"jane@acme.com", "hello@acme.com"}
    assert "other@example.com" not in result.emails
    assert result.pages_fetched == ["https://acme.com/team"]
    assert result.used_firecrawl is False


def test_scrape_falls_back_to_firecrawl_when_httpx_dry():
    """httpx returns a JS-only shell (no emails) → FirecrawlClient DI fallback fires."""
    fake_fc = MagicMock()
    fake_fc.scrape.return_value = MagicMock(
        markdown="Our CEO Jane — mailto:jane@acme.com",
        metadata={},
        status_code=200,
    )

    def handler(request: httpx.Request) -> httpx.Response:
        return httpx.Response(
            200,
            text="<html><body><div id=root></div></body></html>",
            headers={"Content-Type": "text/html"},
        )

    with httpx.Client(transport=httpx.MockTransport(handler)) as client:
        result = scrape_team_pages("acme.com", ["/team"], client=client, firecrawl=fake_fc)

    assert "jane@acme.com" in result.emails
    assert result.used_firecrawl is True


def test_scrape_handles_total_failure():
    def handler(request: httpx.Request) -> httpx.Response:
        return httpx.Response(503, text="unavailable")

    with httpx.Client(transport=httpx.MockTransport(handler)) as client:
        result = scrape_team_pages("deadbrand.com", ["/about"], client=client)

    assert result.emails == []
    assert result.pages_fetched == []


def test_scrape_uses_firecrawl_client_when_httpx_returns_nothing():
    """Legacy fallback path: httpx fails → FirecrawlClient.scrape() is called."""
    fake_fc = MagicMock()
    fake_fc.scrape.return_value = MagicMock(
        markdown="contact: pr@brilliantearth.com",
        metadata={},
        status_code=200,
    )

    def _httpx_handler(_request):
        return httpx.Response(404)

    httpx_client = httpx.Client(transport=httpx.MockTransport(_httpx_handler))

    result = scrape_team_pages(
        "brilliantearth.com",
        ["/team", "/contact"],
        client=httpx_client,
        firecrawl=fake_fc,
    )
    assert fake_fc.scrape.called
    assert any("brilliantearth.com" in e for e in result.emails)
    assert result.used_firecrawl is True


def test_scrape_skips_firecrawl_when_httpx_returns_emails():
    """If httpx finds emails, skip Firecrawl entirely."""
    fake_fc = MagicMock()

    def _httpx_handler(_request):
        return httpx.Response(
            200,
            text="<a href='mailto:hello@brilliantearth.com'>Hello</a>",
            headers={"content-type": "text/html"},
        )

    httpx_client = httpx.Client(transport=httpx.MockTransport(_httpx_handler))

    result = scrape_team_pages(
        "brilliantearth.com",
        ["/team"],
        client=httpx_client,
        firecrawl=fake_fc,
    )
    assert fake_fc.scrape.call_count == 0
    assert "hello@brilliantearth.com" in result.emails
    assert result.used_firecrawl is False


def test_scrape_works_without_firecrawl_param():
    """Backward-compat: if firecrawl=None, no Firecrawl fallback (httpx-only)."""
    def _httpx_handler(_request):
        return httpx.Response(404)

    httpx_client = httpx.Client(transport=httpx.MockTransport(_httpx_handler))

    result = scrape_team_pages(
        "brilliantearth.com",
        ["/team"],
        client=httpx_client,
        firecrawl=None,
    )
    assert result.used_firecrawl is False
    assert result.emails == []


def test_use_map_calls_firecrawl_map_then_scrapes_filtered_urls():
    """use_map=True → call firecrawl.map() then scrape only URLs matching team/about/contact."""
    from unittest.mock import MagicMock

    fake_fc = MagicMock()
    fake_fc.map.return_value = MagicMock(
        domain="roma.com",
        urls=[
            "https://roma.com/",
            "https://roma.com/blog",
            "https://roma.com/about-us",
            "https://roma.com/our-team",
            "https://roma.com/contact",
            "https://roma.com/products",
            "https://roma.com/leadership",
        ],
    )
    fake_fc.scrape.return_value = MagicMock(
        markdown="contact us at hello@roma.com",
        metadata={},
        status_code=200,
    )

    import httpx
    def _httpx_handler(_request):
        return httpx.Response(404)
    httpx_client = httpx.Client(transport=httpx.MockTransport(_httpx_handler))

    result = scrape_team_pages(
        "roma.com",
        paths=["/team"],  # ignored in use_map mode
        client=httpx_client,
        firecrawl=fake_fc,
        use_map=True,
    )

    fake_fc.map.assert_called_once()
    scraped_urls = [c.args[0] for c in fake_fc.scrape.call_args_list]
    assert any("our-team" in u for u in scraped_urls)
    assert any("about-us" in u for u in scraped_urls)
    assert any("contact" in u for u in scraped_urls)
    assert not any("blog" in u for u in scraped_urls)
    assert not any("products" in u for u in scraped_urls)
    assert "hello@roma.com" in result.emails
    assert result.used_firecrawl is True


def test_use_map_caps_at_5_urls():
    """If 10 URLs match the filter, only top 5 are scraped."""
    from unittest.mock import MagicMock

    fake_fc = MagicMock()
    fake_fc.map.return_value = MagicMock(
        domain="roma.com",
        urls=[f"https://roma.com/team-{i}" for i in range(10)],
    )
    fake_fc.scrape.return_value = MagicMock(markdown="", metadata={}, status_code=200)

    import httpx
    def _httpx_handler(_request):
        return httpx.Response(404)
    httpx_client = httpx.Client(transport=httpx.MockTransport(_httpx_handler))

    scrape_team_pages(
        "roma.com",
        paths=[],
        client=httpx_client,
        firecrawl=fake_fc,
        use_map=True,
    )
    assert fake_fc.scrape.call_count == 5


def test_use_extract_calls_firecrawl_extract_with_schema():
    """use_extract=True → call firecrawl.extract() once with all URLs and a contacts schema."""
    from unittest.mock import MagicMock

    fake_fc = MagicMock()
    fake_fc.extract.return_value = MagicMock(
        data={
            "contacts": [
                {"name": "Anna Doe", "email": "anna@roma.com", "title": "CEO"},
                {"name": "Bob Smith", "email": "bob@roma.com", "title": "CTO"},
            ]
        },
        sources=["https://roma.com/team", "https://roma.com/about"],
    )

    import httpx
    httpx_client = httpx.Client(transport=httpx.MockTransport(lambda r: httpx.Response(404)))

    result = scrape_team_pages(
        "roma.com",
        paths=["/team", "/about"],
        client=httpx_client,
        firecrawl=fake_fc,
        use_extract=True,
    )

    fake_fc.extract.assert_called_once()
    args, kwargs = fake_fc.extract.call_args
    urls = args[0] if args else kwargs.get("urls")
    assert "https://roma.com/team" in urls
    assert "https://roma.com/about" in urls
    schema = args[1] if len(args) > 1 else kwargs.get("schema")
    assert schema is not None
    assert "contacts" in schema.get("properties", {})
    assert "anna@roma.com" in result.emails
    assert "bob@roma.com" in result.emails
    assert result.used_firecrawl is True


def test_use_map_filter_rejects_substring_false_positives():
    """The _TEAM_PATH_RE filter should not match URLs that just contain
    'team' or 'about' as a substring without a word boundary."""
    from unittest.mock import MagicMock

    fake_fc = MagicMock()
    fake_fc.map.return_value = MagicMock(
        domain="roma.com",
        urls=[
            "https://roma.com/teaminator-bot",      # false-positive in old regex
            "https://roma.com/aboutification",      # false-positive in old regex
            "https://roma.com/our-team",            # legitimate match
            "https://roma.com/about-us",            # legitimate match
        ],
    )
    fake_fc.scrape.return_value = MagicMock(markdown="", metadata={}, status_code=200)

    import httpx
    httpx_client = httpx.Client(transport=httpx.MockTransport(lambda r: httpx.Response(404)))

    scrape_team_pages(
        "roma.com",
        paths=[],
        client=httpx_client,
        firecrawl=fake_fc,
        use_map=True,
    )
    scraped_urls = [c.args[0] for c in fake_fc.scrape.call_args_list]
    assert "https://roma.com/our-team" in scraped_urls
    assert "https://roma.com/about-us" in scraped_urls
    assert "https://roma.com/teaminator-bot" not in scraped_urls
    assert "https://roma.com/aboutification" not in scraped_urls


def test_use_extract_combined_with_use_map():
    """use_map=True + use_extract=True → map then extract on filtered URLs."""
    from unittest.mock import MagicMock

    fake_fc = MagicMock()
    fake_fc.map.return_value = MagicMock(
        domain="roma.com",
        urls=["https://roma.com/our-team", "https://roma.com/blog", "https://roma.com/about-us"],
    )
    fake_fc.extract.return_value = MagicMock(
        data={"contacts": [{"name": "X", "email": "x@roma.com", "title": "Founder"}]},
        sources=[],
    )

    import httpx
    httpx_client = httpx.Client(transport=httpx.MockTransport(lambda r: httpx.Response(404)))

    result = scrape_team_pages(
        "roma.com",
        paths=[],
        client=httpx_client,
        firecrawl=fake_fc,
        use_map=True,
        use_extract=True,
    )
    fake_fc.map.assert_called_once()
    fake_fc.extract.assert_called_once()
    extract_urls = fake_fc.extract.call_args.args[0]
    assert "https://roma.com/our-team" in extract_urls
    assert "https://roma.com/about-us" in extract_urls
    assert "https://roma.com/blog" not in extract_urls
    assert "x@roma.com" in result.emails
