"""Apply permission profiles with session lifetime and legacy grant bridging."""

from __future__ import annotations

import json
from dataclasses import dataclass, field
from datetime import datetime, timezone
from pathlib import Path
from typing import Any, Callable

from iuve_connect_agents.shared.permission_profiles import (
    PROFILE_KEYS,
    allowed_session_lifetimes,
    infer_profile_from_ui_keys,
    lifetime_to_expiry_preset,
    profile_capability_bundles,
    profile_risk_level,
    profile_ui_keys,
    resolve_lifetime_for_device,
)
from iuve_connect_agents.shared.ui_capability_grants import UI_CAPABILITY_KEYS

from ..capability_manager import CAPABILITY_DEFINITIONS, CapabilityStateManager
from .capability_store import CapabilityStore

DEFAULT_STATE_DIR = Path.home() / ".config" / "iuve-connect-agent"
PROFILE_STATE_FILENAME = "permission-profile.json"
SCHEMA_VERSION = "0.2.2.56"


@dataclass
class PermissionProfileState:
    profile_key: str = "minimal"
    lifetime: str = "app_exit"
    applied_at: str = ""
    risk_level: str = "low"
    audit_status: str = "active"

    def to_dict(self) -> dict[str, Any]:
        return {
            "schema_version": SCHEMA_VERSION,
            "profile_key": self.profile_key,
            "lifetime": self.lifetime,
            "applied_at": self.applied_at,
            "risk_level": self.risk_level,
            "audit_status": self.audit_status,
        }

    @classmethod
    def from_dict(cls, raw: dict[str, Any]) -> PermissionProfileState:
        return cls(
            profile_key=str(raw.get("profile_key") or "minimal").strip().lower(),
            lifetime=str(raw.get("lifetime") or "app_exit").strip().lower(),
            applied_at=str(raw.get("applied_at") or ""),
            risk_level=str(raw.get("risk_level") or "low"),
            audit_status=str(raw.get("audit_status") or "active"),
        )


ApplyGrantsFn = Callable[[list[str], str], dict[str, Any]]


@dataclass
class PermissionProfileService:
    """Profile assignment workflow — does not alter PolicyEngine execution paths."""

    capability_store: CapabilityStore
    state_dir: Path = field(default_factory=lambda: DEFAULT_STATE_DIR)
    apply_grants: ApplyGrantsFn | None = None

    def __post_init__(self) -> None:
        self._path = (self.state_dir / PROFILE_STATE_FILENAME).expanduser()
        self._path.parent.mkdir(parents=True, exist_ok=True)

    @property
    def path(self) -> Path:
        return self._path

    @property
    def manager(self) -> CapabilityStateManager:
        return self.capability_store.manager

    def _load_raw(self) -> dict[str, Any]:
        if not self._path.is_file():
            return {}
        try:
            data = json.loads(self._path.read_text(encoding="utf-8"))
            return data if isinstance(data, dict) else {}
        except Exception:
            return {}

    def _save(self, state: PermissionProfileState) -> None:
        payload = state.to_dict()
        self._path.write_text(json.dumps(payload, indent=2), encoding="utf-8")
        try:
            self._path.chmod(0o600)
        except OSError:
            pass

    def read_state(self) -> PermissionProfileState:
        raw = self._load_raw()
        if raw:
            return PermissionProfileState.from_dict(raw)
        return self.bridge_legacy_grants_on_load()

    def bridge_legacy_grants_on_load(self) -> PermissionProfileState:
        """Infer profile from existing UI grants when profile state file is absent."""
        granted = {
            key
            for key in UI_CAPABILITY_KEYS
            if self.capability_store.is_ui_enabled(key)
        }
        profile_key = infer_profile_from_ui_keys(granted) if granted else "minimal"
        lifetime = self._infer_lifetime_from_grants()
        state = PermissionProfileState(
            profile_key=profile_key,
            lifetime=lifetime,
            applied_at=datetime.now(timezone.utc).isoformat(),
            risk_level=profile_risk_level(profile_key),
            audit_status="active" if granted else "none",
        )
        if granted:
            self._save(state)
        return state

    def _infer_lifetime_from_grants(self) -> str:
        presets: set[str] = set()
        for key in UI_CAPABILITY_KEYS:
            cap = self.manager.get(key)
            if cap is None or not cap.enabled:
                continue
            preset = str(cap.expiry_preset or cap.expiry_type or "").strip().lower()
            if preset:
                presets.add(preset)
        if "permanent" in presets:
            return "permanent"
        if "reboot" in presets:
            return "device_shutdown"
        if "one_shot" in presets:
            return "single_task"
        if "app_exit" in presets:
            return "app_exit"
        return "app_exit"

    def build_summary(
        self,
        *,
        trust_state: str = "",
        paired: bool = False,
        authenticated: bool = False,
    ) -> dict[str, Any]:
        state = self.read_state()
        profile_key = state.profile_key if state.profile_key in PROFILE_KEYS else "minimal"
        granted = {
            key
            for key in UI_CAPABILITY_KEYS
            if self.capability_store.is_ui_enabled(key)
        }
        if granted:
            profile_key = infer_profile_from_ui_keys(granted)
        return {
            "profile_key": profile_key,
            "bundles": list(profile_capability_bundles(profile_key)),
            "ui_keys": sorted(profile_ui_keys(profile_key)),
            "lifetime": state.lifetime,
            "risk_level": profile_risk_level(profile_key),
            "audit_status": "active" if granted else state.audit_status,
            "granted_count": len(granted),
            "allowed_lifetimes": sorted(
                allowed_session_lifetimes(
                    trust_state=trust_state,
                    paired=paired,
                    authenticated=authenticated,
                )
            ),
        }

    def apply_profile(
        self,
        profile_key: str,
        *,
        lifetime: str = "app_exit",
        trust_state: str = "",
        paired: bool = False,
        authenticated: bool = False,
        blocked_ui_keys: set[str] | None = None,
        sync_backend: bool = True,
    ) -> dict[str, Any]:
        preset = str(profile_key or "").strip().lower()
        if preset not in PROFILE_KEYS:
            return {"ok": False, "error": "unknown_profile", "profile_key": preset}

        resolved_lifetime = resolve_lifetime_for_device(
            lifetime,
            trust_state=trust_state,
            paired=paired,
            authenticated=authenticated,
        )
        expiry_preset = lifetime_to_expiry_preset(resolved_lifetime)
        blocked = blocked_ui_keys or set()
        selected = sorted(
            key for key in profile_ui_keys(preset) if key not in blocked
        )
        if not selected:
            return {"ok": False, "error": "profile_blocked_by_plan", "profile_key": preset}

        apply_result: dict[str, Any]
        if self.apply_grants is not None:
            apply_result = self.apply_grants(selected, expiry_preset)
        else:
            apply_result = self._apply_grants_direct(selected, expiry_preset)

        now = datetime.now(timezone.utc).isoformat()
        state = PermissionProfileState(
            profile_key=preset,
            lifetime=resolved_lifetime,
            applied_at=now,
            risk_level=profile_risk_level(preset),
            audit_status="active",
        )
        self._save(state)
        self.manager._audit(
            "permission_profile_applied",
            profile_key=preset,
            lifetime=resolved_lifetime,
            expiry_preset=expiry_preset,
            ui_keys=selected,
        )
        return {
            "ok": True,
            "profile_key": preset,
            "lifetime": resolved_lifetime,
            "expiry_preset": expiry_preset,
            "ui_keys": selected,
            "grants": apply_result,
            "sync_backend": sync_backend,
        }

    def _apply_grants_direct(self, selected_keys: list[str], expiry_preset: str) -> dict[str, Any]:
        granted: list[str] = []
        revoked: list[str] = []
        selected = {str(k).strip() for k in selected_keys if str(k).strip() in CAPABILITY_DEFINITIONS}
        for key in CAPABILITY_DEFINITIONS:
            if key in selected:
                cap = self.capability_store.grant_ui(key, expiry_preset=expiry_preset)
                if cap is not None:
                    granted.append(key)
            elif self.capability_store.is_ui_enabled(key):
                cap = self.capability_store.revoke_ui(key)
                if cap is not None:
                    revoked.append(key)
        return {"granted": granted, "revoked": revoked}

    def revoke_app_exit_grants(self) -> int:
        """Revoke grants tied to app-exit lifetime (called on console shutdown)."""
        count = 0
        for key in UI_CAPABILITY_KEYS:
            cap = self.manager.get(key)
            if cap is None or not cap.enabled:
                continue
            if str(cap.expiry_type or "").strip().lower() != "app_exit":
                continue
            self.capability_store.revoke_ui(key)
            count += 1
        if count:
            raw = self._load_raw()
            if raw:
                state = PermissionProfileState.from_dict(raw)
                state.audit_status = "revoked_app_exit"
                self._save(state)
        return count

    def check_lifetime_expiry(self) -> list[str]:
        """Return UI keys whose grants expired (timestamp / one_shot / reboot)."""
        expired: list[str] = []
        for key in UI_CAPABILITY_KEYS:
            cap = self.manager.get(key)
            if cap is None:
                continue
            if cap.enabled and cap.is_expired():
                expired.append(key)
        return expired
