from __future__ import annotations

import json
from typing import Protocol
from urllib.error import URLError
from urllib.request import Request, urlopen

from jarvis_gateway.adapters.base import BaseAdapter
from jarvis_gateway.adapters.finance_sanitizer import sanitize_finance_payload
from jarvis_gateway.contracts import ModuleId, ModuleSnapshot, utcnow
from jarvis_gateway.redaction import redact_error_message

ALLOWED_ENDPOINTS = [
    "/api/health",
    "/api/provider/status",
    "/api/runtime/status",
    "/api/system/status",
    "/api/budget/import-status-audit",
]


class FinanceHttpClient(Protocol):
    def get_json(self, url: str, timeout: float) -> object: ...


class UrllibFinanceHttpClient:
    def get_json(self, url: str, timeout: float) -> object:
        request = Request(url, method="GET", headers={"accept": "application/json"})
        with urlopen(request, timeout=timeout) as response:  # noqa: S310 - configured local/internal URL only, no secrets
            return json.loads(response.read().decode("utf-8"))


class FinanceLiveReadonlyAdapter(BaseAdapter):
    module_id = ModuleId.FINANCE
    title = "FinanceManager"

    def __init__(self, *, base_url: str, legacy_dashboard_url: str | None = None, timeout_seconds: float = 3, http_client: FinanceHttpClient | None = None) -> None:
        if not base_url:
            raise ValueError("FINANCE_API_BASE_URL is required")
        self.base_url = base_url.rstrip("/")
        self.legacy_dashboard_url = legacy_dashboard_url
        self.timeout_seconds = timeout_seconds
        self.http_client = http_client or UrllibFinanceHttpClient()

    def get_snapshot(self) -> ModuleSnapshot:
        now = utcnow()
        collected: dict[str, object] = {}
        try:
            for endpoint in ALLOWED_ENDPOINTS:
                # Always GET; no mutation endpoints exist in this adapter.
                collected[endpoint.removeprefix("/api/")] = self.http_client.get_json(f"{self.base_url}{endpoint}", self.timeout_seconds)
            return sanitize_finance_payload(collected, legacy_dashboard_url=self.legacy_dashboard_url, last_attempt_at=now)
        except Exception as exc:  # noqa: BLE001 - adapter must not crash overview
            return self._offline_snapshot(exc, now)

    def _offline_snapshot(self, exc: Exception, now):
        from jarvis_gateway.contracts import ActionDescriptor, ActionMode, AttentionItem, DisplayPolicy, KpiItem, ModuleStatus, Sensitivity, Severity, SourceHealth, SourceType

        safe_error = redact_error_message(str(exc))
        action = ActionDescriptor(label="Open Finance", href="/finance", mode=ActionMode.LINK_ONLY, sensitivity=Sensitivity.SENSITIVE, blocked_reason=None, preview_required=False, confirm_required=False, audit_required=False)
        snapshot = ModuleSnapshot(
            module_id=ModuleId.FINANCE,
            title=self.title,
            status=ModuleStatus.OFFLINE,
            sensitivity=Sensitivity.SENSITIVE,
            display_policy=DisplayPolicy.SUMMARY,
            last_success_at=None,
            last_attempt_at=now,
            stale_after_seconds=300,
            degraded_reason="finance_manager_unreachable",
            source_health=SourceHealth(reachable=False, latency_ms=None, source_type=SourceType.HTTP_API, version="finance-readonly-v1", contract_version="jarvis.module_snapshot.v1", last_success_at=None, last_error_redacted=safe_error, stale=True),
            kpis=[
                KpiItem(key="runtime_reachable", label="Runtime erreichbar", value="Nein", unit=None, severity=Severity.ERROR, sensitivity=Sensitivity.INTERNAL, display_policy=DisplayPolicy.SUMMARY, help_text="Read-only reachability status."),
                KpiItem(key="provider_connected", label="Provider verbunden", value="Unbekannt", unit=None, severity=Severity.WARNING, sensitivity=Sensitivity.INTERNAL, display_policy=DisplayPolicy.SUMMARY, help_text="Status only."),
                KpiItem(key="review_items", label="Review Items", value=0, unit="items", severity=Severity.INFO, sensitivity=Sensitivity.SENSITIVE, display_policy=DisplayPolicy.SUMMARY, help_text="Count only; no amounts."),
            ],
            attention_items=[AttentionItem(id="att_finance_unreachable", module_id=ModuleId.FINANCE, title="FinanceManager nicht erreichbar", message="FinanceManager ist read-only nicht erreichbar.", severity=Severity.ERROR, sensitivity=Sensitivity.SENSITIVE, created_at=now, action=action, dedupe_key="finance.unreachable")],
            links=[action],
            primary_action=action,
            contract_version="jarvis.module_snapshot.v1",
        )
        from jarvis_gateway.adapters.finance_sanitizer import assert_no_sensitive_finance_output
        from jarvis_gateway.redaction import assert_safe_module_snapshot
        payload = snapshot.model_dump(mode="json")
        assert_no_sensitive_finance_output(payload)
        assert_safe_module_snapshot(payload)
        return snapshot
