from __future__ import annotations

import json
import time
from pathlib import Path

from oncecheck.rules import sync as policy_sync


def _patch_storage(monkeypatch, tmp_path: Path) -> None:
    policy_dir = tmp_path / "policies"
    monkeypatch.setattr(policy_sync, "_policy_dir", lambda: policy_dir)
    monkeypatch.setattr(policy_sync, "_index_path", lambda: policy_dir / "policy_index.json")
    monkeypatch.setattr(policy_sync, "_source_content_path", lambda source_id: policy_dir / f"{source_id}.html")


def test_sync_policies_writes_index_and_snapshots(monkeypatch, tmp_path):
    _patch_storage(monkeypatch, tmp_path)

    def fake_fetch(url: str, timeout_seconds: int = 20, etag: str = "", last_modified: str = ""):
        return 200, f"<html>{url}</html>".encode(), {"etag": "abc", "last_modified": "", "content_type": "text/html"}

    monkeypatch.setattr(policy_sync, "_fetch_url", fake_fetch)
    result = policy_sync.sync_policies(force=True)

    assert result["failed"] == 0
    assert result["updated"] == len(policy_sync.default_policy_sources())

    index = json.loads((tmp_path / "policies" / "policy_index.json").read_text())
    assert len(index) == len(policy_sync.default_policy_sources())
    for source in policy_sync.default_policy_sources():
        assert (tmp_path / "policies" / f"{source.id}.html").exists()


def test_sync_policies_tracks_unchanged(monkeypatch, tmp_path):
    _patch_storage(monkeypatch, tmp_path)
    policy_dir = tmp_path / "policies"
    policy_dir.mkdir(parents=True, exist_ok=True)

    now = int(time.time())
    seeded = {
        source.id: {
            "id": source.id,
            "platform": source.platform,
            "name": source.name,
            "url": source.url,
            "synced_at": now,
            "last_checked_at": now,
            "last_status": 200,
            "etag": "seed",
            "last_modified": "",
        }
        for source in policy_sync.default_policy_sources()
    }
    (policy_dir / "policy_index.json").write_text(json.dumps(seeded))

    def fake_fetch(url: str, timeout_seconds: int = 20, etag: str = "", last_modified: str = ""):
        assert etag == "seed"
        return 304, b"", {}

    monkeypatch.setattr(policy_sync, "_fetch_url", fake_fetch)
    result = policy_sync.sync_policies(force=False)
    assert result["unchanged"] == len(policy_sync.default_policy_sources())
    assert result["updated"] == 0


def test_policy_status_reports_missing_and_stale(monkeypatch, tmp_path):
    _patch_storage(monkeypatch, tmp_path)
    policy_dir = tmp_path / "policies"
    policy_dir.mkdir(parents=True, exist_ok=True)
    now = int(time.time())
    stale_sync = now - (policy_sync.STALE_AFTER_DAYS + 2) * 24 * 3600

    sources = policy_sync.default_policy_sources()
    first = sources[0]
    second = sources[1]
    index = {
        first.id: {
            "id": first.id,
            "platform": first.platform,
            "name": first.name,
            "url": first.url,
            "synced_at": stale_sync,
            "last_checked_at": stale_sync,
            "last_status": 200,
        },
        second.id: {
            "id": second.id,
            "platform": second.platform,
            "name": second.name,
            "url": second.url,
            "synced_at": now,
            "last_checked_at": now,
            "last_status": 200,
        },
    }
    (policy_dir / "policy_index.json").write_text(json.dumps(index))

    status = policy_sync.get_policy_sync_status(stale_after_days=policy_sync.STALE_AFTER_DAYS)
    assert status["stale"] >= 1
    assert status["missing"] >= 1
    assert status["healthy"] is False
