from __future__ import annotations

import datetime as dt
import types

import pandas as pd
import pytest

import worai.seoreport.core as core


def test_get_period_ranges_math() -> None:
    last = dt.date(2026, 2, 24)
    ranges = core.get_period_ranges(last, 7)
    assert ranges.current.end == last
    assert ranges.current.start == dt.date(2026, 2, 18)
    assert ranges.pop.end == dt.date(2026, 2, 17)


def test_fetch_gsc_data_paginates(monkeypatch: pytest.MonkeyPatch) -> None:
    calls = {"n": 0}

    def _stub(_session, _site, payload):
        calls["n"] += 1
        if payload["startRow"] == 0:
            return {"rows": [{"keys": ["q1"], "clicks": 1, "impressions": 2, "ctr": 0.5, "position": 3}]}
        return {"rows": []}

    monkeypatch.setattr(core, "gsc_post", _stub)
    df = core.fetch_gsc_data(object(), "s", dt.date(2026, 1, 1), dt.date(2026, 1, 2), ["query"], row_limit=1)
    assert calls["n"] == 2
    assert list(df["query"]) == ["q1"]


def test_run_with_retries(monkeypatch: pytest.MonkeyPatch) -> None:
    count = {"n": 0}
    monkeypatch.setattr(core.time, "sleep", lambda _x: None)
    monkeypatch.setattr(core.random, "random", lambda: 0.0)

    def _flaky():
        count["n"] += 1
        if count["n"] < 2:
            raise RuntimeError("x")
        return 7

    assert core._run_with_retries(_flaky, retries=2) == 7


def test_aggregate_and_diffs() -> None:
    df = pd.DataFrame(
        [
            {"query": "q", "page": "/a", "clicks": 2, "impressions": 4, "ctr": 0.5, "position": 4},
            {"query": "q", "page": "/b", "clicks": 1, "impressions": 1, "ctr": 1.0, "position": 2},
        ]
    )
    out = core.aggregate_data(df, None)
    assert out.loc["q", "clicks"] == 3
    assert out.loc["q", "impressions"] == 5

    diffs = core.calculate_diffs(pd.Series({"clicks": 10, "impressions": 0, "ctr": 0.0, "position": 1.0}), pd.Series())
    assert diffs["abs"]["clicks"] == 10


def test_fetch_inspection_and_health(monkeypatch: pytest.MonkeyPatch) -> None:
    class _Resp:
        status_code = 200

        @staticmethod
        def json():
            return {"inspectionResult": {"indexStatusResult": {"verdict": "FAIL", "coverageState": "Soft 404"}}}

        text = "ok"

    class _Session:
        @staticmethod
        def post(_url, json=None):
            return _Resp()

    monkeypatch.setattr(core.time, "sleep", lambda _x: None)
    summary = core.fetch_inspection_data(_Session(), "site", ["https://example.com/a"])
    assert summary.error == 1
    assert "Soft 404" in summary.issues

    class _HeadResp:
        status_code = 405

    class _GetResp:
        status_code = 200

        @staticmethod
        def close():
            return None

    monkeypatch.setattr(core.requests, "head", lambda *_a, **_k: _HeadResp())
    monkeypatch.setattr(core.requests, "get", lambda *_a, **_k: _GetResp())
    seq = iter([1.0, 1.2, 2.0, 2.1])
    monkeypatch.setattr(core.time, "time", lambda: next(seq))
    health = core.run_live_health_check(["https://example.com/a", "https://example.com/b"])
    assert health.total_checked == 2
    assert health.error_pages == 0


def test_analyze_period_with_stubbed_fetch(monkeypatch: pytest.MonkeyPatch) -> None:
    date = dt.date(2026, 2, 24)
    ranges = core.get_period_ranges(date, 7)

    curr = pd.DataFrame([{"query": "q", "clicks": 10, "impressions": 20, "ctr": 0.5, "position": 4.0}])
    prev = pd.DataFrame([{"query": "q", "clicks": 5, "impressions": 10, "ctr": 0.5, "position": 5.0}])
    yoy = pd.DataFrame([{"query": "q", "clicks": 4, "impressions": 9, "ctr": 0.4, "position": 6.0}])
    trend = pd.DataFrame([{"date": "2026-02-24", "clicks": 1, "impressions": 2, "ctr": 0.5, "position": 3.0}])

    monkeypatch.setattr(core, "_run_tasks", lambda _tasks, max_workers: {"curr": curr, "pop": prev, "yoy": yoy, "trend": trend})
    monkeypatch.setattr(core, "AuthorizedSession", lambda _creds: object())
    analysis = core.analyze_period("Last 7 days", ranges, creds=object(), site="s", url_regex=None, max_workers=2)
    assert analysis.name == "Last 7 days"
    assert analysis.overall["current"]["clicks"] == 10
    assert analysis.daily_trend[0].date == "2026-02-24"


def test_generate_report_data_with_ga(monkeypatch: pytest.MonkeyPatch) -> None:
    date = dt.date(2026, 2, 24)

    ranges = core.PeriodRanges(
        current=core.DateRange(date, date),
        pop=core.DateRange(date, date),
        yoy=core.DateRange(date, date),
    )

    period = core.PeriodAnalysis(name="Last 7 days", dates=ranges, overall={}, winning=[], losing=[], opportunities=[])
    monkeypatch.setattr(core, "load_credentials", lambda *_a, **_k: object())
    monkeypatch.setattr(core, "AuthorizedSession", lambda _c: object())
    monkeypatch.setattr(core, "find_last_available_date", lambda _s, _site: date)
    monkeypatch.setattr(core, "_run_tasks", lambda _tasks, max_workers: {"7": period, "30": period, "90": period})
    monkeypatch.setattr(core, "build_analytics", lambda *_a, **_k: {"periods": [{"name": "Last 7 days"}]})
    monkeypatch.setattr(core, "load_ga_credentials", lambda *_a, **_k: object())
    monkeypatch.setattr(core, "_resolve_ga_auth", lambda _opts: ("ga.json", "tok.json"))

    data = core.generate_report_data(
        core.ReportOptions(site="s", ga_id="123", client_secrets="c.json", token="t.json", inspect_limit=0)
    )
    assert data["analytics"] is not None
