from __future__ import annotations

from jarvis_gateway.adapters.base import BaseAdapter
from jarvis_gateway.adapters.autoshorts_disabled import AutoShortsDisabledAdapter
from jarvis_gateway.adapters.autoshorts_live_readonly import AutoShortsLiveReadonlyAdapter
from jarvis_gateway.adapters.autoshorts_mock import AutoShortsMockAdapter
from jarvis_gateway.adapters.autoshorts_probe import AutoShortsLocalProbeAdapter
from jarvis_gateway.adapters.mock_family import MockFamilyAdapter
from jarvis_gateway.adapters.mock_finance import MockFinanceAdapter
from jarvis_gateway.adapters.mock_health import MockHealthAdapter
from jarvis_gateway.adapters.mock_system import MockSystemAdapter
from jarvis_gateway.adapters.finance_disabled import DisabledFinanceAdapter
from jarvis_gateway.adapters.finance_live_readonly import FinanceLiveReadonlyAdapter
from jarvis_gateway.adapters.health_disabled import DisabledHealthAdapter
from jarvis_gateway.adapters.health_probe import HealthLocalProbeAdapter
from jarvis_gateway.adapters.health_safe_marker import HealthSafeMarkerAdapter
from jarvis_gateway.config import load_runtime_config
from jarvis_gateway.contracts import RuntimeConfig
from jarvis_gateway.contracts import (
    ActionDescriptor,
    ActionMode,
    ActivityEvent,
    ErrorEnvelope,
    ModuleId,
    ModuleSnapshot,
    ModuleStatus,
    OverviewResponse,
    RedactionPolicy,
    Sensitivity,
    Severity,
    SourceType,
    utcnow,
)
from jarvis_gateway.legacy_links import validate_legacy_link
from jarvis_gateway.redaction import assert_safe_module_snapshot, assert_safe_overview, load_forbidden_fields, redact_error_message

SEVERITY_ORDER = {
    "critical": 0,
    "error": 1,
    "warning": 2,
    "info": 3,
    "success": 4,
}


class OverviewService:
    """Compose a safe overview from mock-only module adapters.

    Attention items are sorted by severity first, then by newest created_at first.
    Adapter errors are converted into degraded snapshots so the API does not crash.
    """

    def __init__(self, adapters: list[BaseAdapter], config: RuntimeConfig | None = None) -> None:
        self.adapters = adapters
        self.config = config or load_runtime_config()

    @classmethod
    def mock(cls) -> "OverviewService":
        return cls([MockFinanceAdapter(), MockHealthAdapter(), AutoShortsMockAdapter(), MockFamilyAdapter(), MockSystemAdapter()])

    @classmethod
    def from_config(cls, config: RuntimeConfig | None = None) -> "OverviewService":
        current = config or load_runtime_config()
        if current.finance_adapter_mode == "disabled":
            finance = DisabledFinanceAdapter()
        elif current.finance_adapter_mode == "live_readonly":
            finance = FinanceLiveReadonlyAdapter(
                base_url=current.finance_api_base_url or "",
                legacy_dashboard_url=current.finance_legacy_dashboard_url,
                timeout_seconds=current.finance_read_timeout_seconds,
            )
        else:
            finance = MockFinanceAdapter()
        if current.health_adapter_mode == "disabled":
            health = DisabledHealthAdapter()
        elif current.health_adapter_mode == "local_probe":
            health = HealthLocalProbeAdapter(
                runtime_base=current.health_runtime_base,
                legacy_dashboard_url=current.health_legacy_dashboard_url,
            )
        elif current.health_adapter_mode == "safe_marker":
            health = HealthSafeMarkerAdapter(
                marker_path=current.health_safe_status_marker_path,
                max_age_hours=current.health_safe_status_max_age_hours,
            )
        else:
            health = MockHealthAdapter()
        if current.autoshorts_adapter_mode == "disabled":
            autoshorts = AutoShortsDisabledAdapter()
        elif current.autoshorts_adapter_mode == "local_probe":
            autoshorts = AutoShortsLocalProbeAdapter(runtime_base=current.autoshorts_runtime_base)
        elif current.autoshorts_adapter_mode == "live_readonly":
            autoshorts = AutoShortsLiveReadonlyAdapter(base_url=current.autoshorts_api_base_url)
        else:
            autoshorts = AutoShortsMockAdapter()
        return cls([finance, health, autoshorts, MockFamilyAdapter(current.family_legacy_dashboard_url), MockSystemAdapter()], config=current)

    def build_overview(self) -> OverviewResponse:
        modules: list[ModuleSnapshot] = []
        errors: list[ErrorEnvelope] = []
        for adapter in self.adapters[:5]:
            try:
                snapshot = adapter.get_snapshot()
            except Exception as exc:  # noqa: BLE001 - safe degraded conversion
                snapshot = adapter.error_snapshot(exc)
                errors.append(
                    ErrorEnvelope(
                        code="ADAPTER_ERROR",
                        message=redact_error_message(str(exc)),
                        severity=Severity.ERROR,
                        module_id=snapshot.module_id,
                        retryable=True,
                        details_redacted=True,
                        occurred_at=utcnow(),
                    )
                )
            snapshot = self._with_legacy_link(snapshot)
            assert_safe_module_snapshot(snapshot.model_dump(mode="json"))
            modules.append(snapshot)

        attention = [item for module in modules for item in module.attention_items]
        attention.sort(key=lambda item: (SEVERITY_ORDER[str(item.severity)], item.created_at), reverse=False)
        attention = attention[:5]
        approvals = [item for module in modules for item in module.approval_items]
        reports = [item for module in modules for item in module.reports]
        activity = [
            ActivityEvent(
                id="act_gateway_overview_demo",
                module_id="system",
                title="Overview composed",
                message="Mock-only overview generated.",
                severity=Severity.INFO,
                sensitivity="internal",
                occurred_at=utcnow(),
                source_type=SourceType.MOCK,
                redacted=True,
            )
        ]
        status = self._aggregate_status(modules)
        policy = RedactionPolicy(
            default_display_policy="summary",
            allow_exact_finance_values=False,
            allow_health_detail_links=False,
            forbidden_fields=["see docs/security/forbidden-fields.md"],
            log_forbidden_fields=["see docs/security/forbidden-fields.md"],
            demo_mode=True,
        )
        overview = OverviewResponse(
            contract_version="jarvis.overview.v1",
            generated_at=utcnow(),
            status=status,
            modules=modules,
            attention_items=attention,
            activity=activity,
            approvals=approvals,
            reports=reports,
            redaction_policy=policy,
            errors=errors,
        )
        assert_safe_overview(overview.model_dump(mode="json"))
        return overview

    def _with_legacy_link(self, snapshot: ModuleSnapshot) -> ModuleSnapshot:
        configured = {
            ModuleId.FINANCE: (self.config.finance_legacy_dashboard_url, "Finance Dashboard öffnen"),
            ModuleId.HEALTH: (self.config.health_legacy_dashboard_url, "Health Detail Links gesperrt"),
            ModuleId.AUTOSHORTS: (self.config.autoshorts_legacy_dashboard_url, "AutoShorts Dashboard öffnen"),
            ModuleId.FAMILY: (self.config.family_legacy_dashboard_url, "Family Dashboard öffnen"),
            ModuleId.SYSTEM: (self.config.jarvis_operator_public_origin, "System Status öffnen"),
        }
        url, label = configured.get(ModuleId(str(snapshot.module_id)), (None, "Fachdashboard öffnen"))
        result = validate_legacy_link(url, allow_tailnet_links=self.config.jarvis_allow_tailnet_links)
        if result.href:
            legacy = ActionDescriptor(label=label, href=result.href, mode=ActionMode.LINK_ONLY, sensitivity=Sensitivity.INTERNAL, blocked_reason=None, preview_required=False, confirm_required=False, audit_required=False)
        else:
            legacy = ActionDescriptor(label=label, href=None, mode=ActionMode.LINK_ONLY, sensitivity=Sensitivity.INTERNAL, blocked_reason="Not configured" if result.status == "not_configured" else "Blocked by safe legacy-link policy", preview_required=False, confirm_required=False, audit_required=False)
        links = [*snapshot.links]
        if not any(link.label == legacy.label for link in links):
            links.append(legacy)
        return snapshot.model_copy(update={"links": links})

    def _aggregate_status(self, modules: list[ModuleSnapshot]) -> ModuleStatus:
        statuses = {ModuleStatus(str(module.status)) for module in modules}
        if ModuleStatus.OFFLINE in statuses:
            return ModuleStatus.OFFLINE
        if ModuleStatus.ATTENTION in statuses or ModuleStatus.DEGRADED in statuses:
            return ModuleStatus.ATTENTION
        return ModuleStatus.OK
