"""Tests for the release_check service (interval clamping + status endpoints + persistence)."""

from __future__ import annotations

import pytest
from fastapi.testclient import TestClient


def test_parse_interval_hours_clamps_and_defaults() -> None:
    from notify_bridge_server.services.release_check import parse_interval_hours

    assert parse_interval_hours("12") == 12
    assert parse_interval_hours("") == 12  # default
    assert parse_interval_hours(None) == 12
    assert parse_interval_hours("0") == 1  # clamped to min
    assert parse_interval_hours("9999") == 168  # clamped to max
    assert parse_interval_hours("not-a-number") == 12  # fallback to default
    assert parse_interval_hours("24") == 24


def test_release_endpoint_anonymous_is_rejected(tmp_data_dir) -> None:  # noqa: ARG001
    """GET /api/settings/release requires auth — same as other settings."""
    from notify_bridge_server.main import app

    with TestClient(app) as client:
        resp = client.get("/api/settings/release")
        # Either 401 (missing token) or 403 (not authenticated) is acceptable.
        assert resp.status_code in (401, 403)


def test_release_force_check_requires_admin(tmp_data_dir) -> None:  # noqa: ARG001
    from notify_bridge_server.main import app

    with TestClient(app) as client:
        resp = client.post("/api/settings/release/check")
        assert resp.status_code in (401, 403)


def test_release_test_requires_admin(tmp_data_dir) -> None:  # noqa: ARG001
    from notify_bridge_server.main import app

    with TestClient(app) as client:
        resp = client.post(
            "/api/settings/release/test",
            json={"provider_kind": "gitea", "provider_url": "https://x.example.com", "provider_repo": "a/b"},
        )
        assert resp.status_code in (401, 403)


# --- Persistence round-trip -------------------------------------------------


@pytest.mark.asyncio
async def test_persist_release_state_round_trip(tmp_data_dir, monkeypatch) -> None:  # noqa: ARG001
    """Write a fake ReleaseInfo, read it back via load_status, assert flags."""
    from notify_bridge_core.release import ReleaseInfo
    from notify_bridge_server.database.engine import init_db
    from notify_bridge_server.services.release_check import (
        load_status,
        persist_release_state,
    )

    await init_db()

    info = ReleaseInfo(
        tag="v0.9.0",
        version="0.9.0",
        name="0.9.0 — Aurora",
        body="Release notes",
        url="https://example.com/x/y/releases/tag/v0.9.0",
        published_at="2026-06-01T00:00:00Z",
        prerelease=False,
        draft=False,
    )
    await persist_release_state(
        checked_at="2026-06-01T00:01:00+00:00",
        error=None,
        info=info,
    )

    # Force the comparator to see an older "current" so update_available
    # comes out True regardless of the actual installed package version.
    monkeypatch.setattr(
        "notify_bridge_server.services.release_check._server_version",
        lambda: "0.7.0",
    )
    status = await load_status()
    assert status.latest == "0.9.0"
    assert status.latest_tag == "v0.9.0"
    assert status.update_available is True
    assert status.error is None
    assert status.latest_body == "Release notes"


@pytest.mark.asyncio
async def test_persist_release_state_clears_on_none_info(tmp_data_dir, monkeypatch) -> None:  # noqa: ARG001
    """A persist call with ``info=None`` must blank all the latest-* fields."""
    from notify_bridge_core.release import ReleaseInfo
    from notify_bridge_server.database.engine import init_db
    from notify_bridge_server.services.release_check import (
        load_status,
        persist_release_state,
    )

    await init_db()

    # Seed a populated row.
    await persist_release_state(
        checked_at="2026-06-01T00:00:00+00:00",
        error=None,
        info=ReleaseInfo(tag="v9.9.9", version="9.9.9"),
    )
    # Now wipe by passing info=None — mimics the "provider_changed" flow.
    await persist_release_state(
        checked_at="2026-06-01T00:02:00+00:00",
        error="provider_changed",
        info=None,
    )
    monkeypatch.setattr(
        "notify_bridge_server.services.release_check._server_version",
        lambda: "0.7.0",
    )
    status = await load_status()
    assert status.latest is None
    assert status.latest_tag is None
    assert status.update_available is False
    assert status.error == "provider_changed"


# --- Version resolver -------------------------------------------------------


def test_resolve_version_prefers_source_pyproject() -> None:
    """When pyproject.toml is alongside the source, prefer the higher of (installed, source)."""
    from notify_bridge_server.version import resolve_version

    v = resolve_version()
    assert v != "0.0.0+unknown"
    # If the editable install is stale (e.g. 0.3.2) but pyproject says 0.7.2,
    # resolve_version must return 0.7.2 (or higher) — the resolver's
    # whole purpose. We test the "not stale" half of the contract here.
    parts = v.split(".")
    assert len(parts) >= 2
    assert parts[0].isdigit()
