from __future__ import annotations

import asyncio
from typing import Any

from nanobot.webui import cli_apps_api


class _FakeManager:
    def __init__(
        self,
        *,
        fresh: bool,
        apps: list[dict[str, Any]] | None = None,
        all_sources_fresh: bool | None = None,
    ) -> None:
        self.fresh = fresh
        self.all_sources_fresh = fresh if all_sources_fresh is None else all_sources_fresh
        self.apps = apps or []
        self.payload_calls: list[bool] = []
        self.fresh_checks: list[bool] = []

    def payload(self, *, cache_only: bool = False) -> dict[str, Any]:
        self.payload_calls.append(cache_only)
        return {
            "apps": list(self.apps),
            "installed_count": 0,
            "catalog_updated_at": "2026-04-18" if self.apps else None,
        }

    def catalog_cache_fresh(self, *, include_optional: bool = False) -> bool:
        self.fresh_checks.append(include_optional)
        return self.all_sources_fresh if include_optional else self.fresh

    def installed_payload(self) -> dict[str, Any]:
        return {
            "apps": [
                {
                    "name": "gimp",
                    "display_name": "GIMP",
                    "category": "image",
                    "description": "Image editing",
                    "requires": "Python",
                    "source": "local",
                    "entry_point": "cli-anything-gimp",
                    "install_supported": True,
                    "installed": True,
                    "available": True,
                    "status": "installed",
                    "logo_url": None,
                    "brand_color": None,
                    "skill_installed": True,
                }
            ],
            "installed_count": 1,
            "catalog_updated_at": None,
        }


def test_cli_apps_payload_uses_cache_and_marks_refresh_pending(monkeypatch) -> None:
    manager = _FakeManager(fresh=False)
    refreshes = []
    monkeypatch.setattr(cli_apps_api, "_manager", lambda: manager)
    monkeypatch.setattr(
        cli_apps_api,
        "_start_catalog_refresh",
        lambda _manager: refreshes.append(True) or True,
    )

    payload = asyncio.run(cli_apps_api.cli_apps_payload())

    assert manager.payload_calls == [True]
    assert manager.fresh_checks == [True]
    assert refreshes == [True]
    assert payload["catalog_refresh_pending"] is True
    assert payload["apps"][0]["name"] == "gimp"


def test_cli_apps_payload_skips_refresh_when_cache_is_fresh(monkeypatch) -> None:
    manager = _FakeManager(
        fresh=True,
        apps=[
            {
                "name": "gimp",
                "display_name": "GIMP",
                "category": "image",
                "description": "Image editing",
                "requires": "Python",
                "source": "harness",
                "entry_point": "cli-anything-gimp",
                "install_supported": True,
                "installed": False,
                "available": False,
                "status": "not_installed",
                "logo_url": None,
                "brand_color": None,
                "skill_installed": False,
            }
        ],
    )
    refreshes = []
    monkeypatch.setattr(cli_apps_api, "_manager", lambda: manager)
    monkeypatch.setattr(
        cli_apps_api,
        "_start_catalog_refresh",
        lambda _manager: refreshes.append(True) or True,
    )

    payload = asyncio.run(cli_apps_api.cli_apps_payload())

    assert manager.payload_calls == [True]
    assert manager.fresh_checks == [True]
    assert refreshes == []
    assert payload["catalog_refresh_pending"] is False
    assert payload["apps"][0]["source"] == "harness"


def test_cli_apps_payload_refreshes_when_optional_cache_is_stale(monkeypatch) -> None:
    manager = _FakeManager(
        fresh=True,
        all_sources_fresh=False,
        apps=[
            {
                "name": "gimp",
                "display_name": "GIMP",
                "category": "image",
                "description": "Image editing",
                "requires": "Python",
                "source": "harness",
                "entry_point": "cli-anything-gimp",
                "install_supported": True,
                "installed": False,
                "available": False,
                "status": "not_installed",
                "logo_url": None,
                "brand_color": None,
                "skill_installed": False,
            }
        ],
    )
    refreshes = []
    monkeypatch.setattr(cli_apps_api, "_manager", lambda: manager)
    monkeypatch.setattr(
        cli_apps_api,
        "_start_catalog_refresh",
        lambda _manager: refreshes.append(True) or True,
    )

    payload = asyncio.run(cli_apps_api.cli_apps_payload())

    assert manager.payload_calls == [True]
    assert manager.fresh_checks == [True]
    assert refreshes == [True]
    assert payload["catalog_refresh_pending"] is True
    assert payload["apps"][0]["source"] == "harness"


def test_cli_apps_payload_reports_not_pending_when_refresh_is_throttled(monkeypatch) -> None:
    manager = _FakeManager(fresh=False)
    monkeypatch.setattr(cli_apps_api, "_manager", lambda: manager)
    monkeypatch.setattr(cli_apps_api, "_start_catalog_refresh", lambda _manager: False)

    payload = asyncio.run(cli_apps_api.cli_apps_payload())

    assert manager.payload_calls == [True]
    assert manager.fresh_checks == [True]
    assert payload["catalog_refresh_pending"] is False
    assert payload["apps"][0]["name"] == "gimp"
