from __future__ import annotations

from datetime import datetime, timezone
from pathlib import Path

import pytest
import requests

from simpleynews import SimpleYNews
from simpleynews.simpleynews import (
    DEFAULT_BASE_URL,
    GERMAN_BASE_URL,
    Ticker,
    parse_news_html,
)

FIXTURES_DIR = Path(__file__).parent / "fixtures"


def load_fixture(name: str) -> str:
    return (FIXTURES_DIR / name).read_text(encoding="utf-8")


class FakeResponse:
    def __init__(self, text: str, url: str, status_code: int = 200) -> None:
        self.text = text
        self.url = url
        self.status_code = status_code

    def raise_for_status(self) -> None:
        if self.status_code >= 400:
            raise requests.HTTPError(f"{self.status_code} for {self.url}")


class FakeSession:
    def __init__(self, responses):
        self._responses = list(responses)
        self.calls: list[str] = []
        self.posts: list[str] = []

    def get(self, url: str, headers=None, timeout=None):  # noqa: ANN001
        del headers, timeout
        self.calls.append(url)
        response = self._responses.pop(0)
        if isinstance(response, Exception):
            raise response
        return response

    def post(self, url: str, data=None, headers=None, timeout=None):  # noqa: ANN001
        del data, headers, timeout
        self.posts.append(url)
        return FakeResponse("<html></html>", url=url)


def test_parse_news_from_embedded_page_state() -> None:
    news = parse_news_html(load_fixture("root_app_main.html"), symbol="BMW.DE")

    assert len(news) == 1
    assert news[0]["title"] == "BMW expands EV lineup"
    assert (
        news[0]["link"]
        == "https://finance.yahoo.com/news/bmw-expands-ev-lineup-103000111.html"
    )
    assert news[0]["publisher"] == "Reuters"
    assert news[0]["type"] == "STORY"
    assert news[0]["relatedTickers"] == ["BMW.DE", "VOW3.DE"]
    assert (
        news[0]["thumbnail"]["resolutions"][0]["url"]
        == "https://images.example.com/bmw-original.jpg"
    )
    assert news[0]["summary"] == "The automaker outlined a broader electric strategy."


def test_parse_news_from_json_ld_fallback() -> None:
    news = parse_news_html(load_fixture("json_ld.html"), symbol="SAP.DE")

    assert len(news) == 1
    assert news[0]["title"] == "SAP signs new cloud deal"
    assert news[0]["publisher"] == "dpa-AFX"
    assert news[0]["relatedTickers"] == ["SAP.DE"]
    assert news[0]["thumbnail"]["resolutions"][0]["url"] == "https://images.example.com/sap.jpg"


def test_parse_news_from_dom_fallback_with_german_time() -> None:
    news = parse_news_html(
        load_fixture("dom_story.html"),
        symbol="SIE.DE",
        base_url=GERMAN_BASE_URL,
    )

    assert len(news) == 1
    assert news[0]["uuid"] == "123e4567-e89b-12d3-a456-426614174000"
    assert news[0]["title"] == "Siemens wins rail contract"
    assert news[0]["publisher"] == "Reuters"
    assert news[0]["relatedTickers"] == ["SIE.DE", "ENR.DE"]
    assert (
        news[0]["link"]
        == "https://de.finance.yahoo.com/news/siemens-wins-contract-080000333.html"
    )

    age_seconds = datetime.now(timezone.utc).timestamp() - news[0]["providerPublishTime"]
    assert age_seconds == pytest.approx(2 * 60 * 60, abs=120)


def test_ticker_caches_successful_results() -> None:
    session = FakeSession(
        [
            FakeResponse(
                load_fixture("dom_story.html"),
                url=f"{DEFAULT_BASE_URL}/quote/AAPL/news?p=AAPL",
            )
        ]
    )
    ticker = Ticker("AAPL", session=session, base_urls=[DEFAULT_BASE_URL])

    first = ticker.news
    second = ticker.news

    assert first is second
    assert len(session.calls) == 1


def test_ticker_does_not_cache_request_failures() -> None:
    session = FakeSession(
        [
            requests.ConnectionError("temporary failure"),
            FakeResponse(
                load_fixture("dom_story.html"),
                url=f"{DEFAULT_BASE_URL}/quote/AAPL/news?p=AAPL",
            ),
        ]
    )
    ticker = Ticker("AAPL", session=session, base_urls=[DEFAULT_BASE_URL])

    assert ticker.news == []
    recovered = ticker.news

    assert len(recovered) == 1
    assert len(session.calls) == 2


def test_de_symbols_try_german_quote_page_first() -> None:
    session = FakeSession(
        [
            FakeResponse(
                load_fixture("dom_story.html"),
                url=f"{GERMAN_BASE_URL}/quote/BMW.DE/news?p=BMW.DE",
            )
        ]
    )

    news = SimpleYNews.Ticker("BMW.DE", session=session).news

    assert len(news) == 1
    assert session.calls[0].startswith(GERMAN_BASE_URL)
