"""Security tab helpers — simplified UX over immutable permission profiles (0.2.2.57)."""

from __future__ import annotations

import datetime as dt
from typing import Any, Callable

from ..active.action_session_store import ACTION_SESSION_PRESET_ALIASES, ActionSessionStore
from ..active.capability_store import CANONICAL_TO_UI_KEYS
from .avatar_plan_session import active_session_covers_preset
from ...shared.ui_capability_grants import PERMISSION_PRESETS, UI_CAPABILITY_KEYS
from ...shared.permission_profiles import (
    PROFILE_KEYS,
    SESSION_LIFETIME_KEYS,
    allowed_session_lifetimes,
    profile_capability_bundles,
    profile_risk_level,
    profile_ui_keys,
    resolve_lifetime_for_device,
)

# Backend profile key → i18n suffix for user-facing labels (full → assistant mode).
PROFILE_DISPLAY_I18N_SUFFIX: dict[str, str] = {
    "minimal": "minimal",
    "standard": "standard",
    "full": "assistant",
    "expert": "expert",
}

# Plain-language capability descriptions (i18n: cap.plain.<suffix>).
CAPABILITY_PLAIN_I18N_SUFFIX: dict[str, str] = {
    "file_access": "file_access",
    "application_control": "application_control",
    "mouse_control": "mouse_control",
    "keyboard_control": "keyboard_control",
    "network_control": "network_control",
    "software_management": "software_management",
    "root_operations": "root_operations",
    "screen_vision": "screen_vision",
}

# Stop-zone categories always requiring owner confirmation (unchanged policy).
STOP_ZONE_CONFIRM_I18N_KEYS: tuple[str, ...] = (
    "security.safety.confirm.publishing",
    "security.safety.confirm.payments",
    "security.safety.confirm.deleting_files",
    "security.safety.confirm.sending_messages",
    "security.safety.confirm.installing_software",
    "security.safety.confirm.system_root",
)

# Simplified task session cards for ordinary users.
SIMPLIFIED_TASK_SESSION_CARDS: tuple[tuple[str, tuple[str, ...] | None], ...] = (
    ("files", ("files",)),
    ("websites", ("web",)),
    ("office_documents", ("apps",)),
    ("maintenance", ("maintenance",)),
    ("development", ("coding",)),
    ("full_assistance", None),
)

FULL_ASSISTANCE_PRESET_KEYS: tuple[str, ...] = ("files", "web", "apps", "desktop")
FULL_ASSISTANCE_DURATION_MINUTES: tuple[int, ...] = (30, 60, 120)
FULL_ASSISTANCE_PROFILE_KEY = "full"

RECOMMENDED_SAFE_PROFILE_KEY = "standard"
RECOMMENDED_SAFE_LIFETIME_KEY = "app_exit"

# Legacy alias — advanced/manual UI may still reference grouped cards.
ACTION_SESSION_UI_CARDS: tuple[tuple[str, tuple[str, ...]], ...] = tuple(
    (key, presets)
    for key, presets in SIMPLIFIED_TASK_SESSION_CARDS
    if presets is not None
)

PROFILE_PRESET_KEYS: tuple[str, ...] = PROFILE_KEYS

TranslateFn = Callable[[str], str]


def normalize_session_preset_key(session: dict[str, Any]) -> str:
    scope = session.get("scope") if isinstance(session.get("scope"), dict) else {}
    preset_key = str(scope.get("preset_key") or "").strip().lower()
    if preset_key:
        return ACTION_SESSION_PRESET_ALIASES.get(preset_key, preset_key)
    title = str(session.get("title") or "").strip().lower()
    legacy_map = {
        "document editing": "files",
        "work with files": "files",
        "browser research": "web",
        "websites & chatgpt": "web",
    }
    return legacy_map.get(title, "")


def sessions_to_replace_for_preset(
    active_sessions: list[dict[str, Any]],
    preset_key: str,
) -> list[dict[str, Any]]:
    """Sessions revoked before starting *preset_key* (dedup by preset / cover)."""
    target = str(preset_key or "").strip().lower()
    if not target:
        return []
    replace: list[dict[str, Any]] = []
    for session in active_sessions:
        if not isinstance(session, dict):
            continue
        session_preset = normalize_session_preset_key(session)
        if session_preset == target:
            replace.append(session)
            continue
        if active_session_covers_preset([session], target):
            replace.append(session)
            continue
        if session_preset and active_session_covers_preset(
            [{"scope": {"preset_key": target}}],
            session_preset,
        ):
            replace.append(session)
    return replace


def dedupe_sessions_by_preset(active_sessions: list[dict[str, Any]]) -> list[dict[str, Any]]:
    """Keep at most one active row per preset type (newest expiry wins)."""
    import datetime as dt

    best: dict[str, tuple[dict[str, Any], dt.datetime | None]] = {}
    for session in active_sessions:
        if not isinstance(session, dict):
            continue
        preset = normalize_session_preset_key(session) or str(session.get("session_id") or "")
        expires_raw = str(session.get("expires_at") or "")
        expires_dt: dt.datetime | None = None
        try:
            expires_dt = dt.datetime.fromisoformat(expires_raw.replace("Z", "+00:00"))
            if expires_dt.tzinfo is None:
                expires_dt = expires_dt.replace(tzinfo=dt.timezone.utc)
        except Exception:
            pass
        current = best.get(preset)
        if current is None:
            best[preset] = (session, expires_dt)
            continue
        _, current_exp = current
        if current_exp is None and expires_dt is not None:
            best[preset] = (session, expires_dt)
        elif expires_dt is not None and current_exp is not None and expires_dt > current_exp:
            best[preset] = (session, expires_dt)
    return [entry[0] for entry in best.values()]


def ui_keys_covered_by_session_capabilities(canonical_caps: set[str]) -> set[str]:
    """Map action-session canonical capabilities to UI grant rows."""
    covered: set[str] = set()
    for canonical in canonical_caps:
        covered.update(CANONICAL_TO_UI_KEYS.get(canonical, frozenset()))
    return covered


def ui_capability_session_active(
    ui_key: str,
    *,
    granted: bool,
    session_canonical_caps: set[str],
) -> bool:
    if granted:
        return False
    return ui_key in ui_keys_covered_by_session_capabilities(session_canonical_caps)


def permission_preset_ui_keys(preset_key: str) -> frozenset[str]:
    return profile_ui_keys(preset_key) or PERMISSION_PRESETS.get(
        str(preset_key or "").strip().lower(), frozenset()
    )


def profile_display_i18n_key(profile_key: str) -> str:
    """User-facing profile label key (maps backend full → assistant)."""
    suffix = PROFILE_DISPLAY_I18N_SUFFIX.get(str(profile_key or "").strip().lower(), profile_key)
    return f"cap.preset.{suffix}"


def capability_plain_i18n_key(ui_key: str) -> str:
    suffix = CAPABILITY_PLAIN_I18N_SUFFIX.get(str(ui_key or "").strip(), ui_key)
    return f"cap.plain.{suffix}"


def format_profile_bundle_labels(profile_key: str, translate_fn: TranslateFn) -> str:
    """Comma-separated human labels for included capability bundles."""
    bundles = profile_capability_bundles(profile_key)
    labels = [translate_fn(f"profile.bundle.{bundle}") for bundle in bundles]
    return ", ".join(labels)


def format_profile_plain_summary(profile_key: str, translate_fn: TranslateFn) -> str:
    """Short plain-language summary for a profile card."""
    key = profile_display_i18n_key(profile_key)
    summary_key = f"{key}.summary"
    translated = translate_fn(summary_key)
    return translated if translated != summary_key else format_profile_bundle_labels(profile_key, translate_fn)


def build_profile_summary(
    *,
    profile_key: str,
    lifetime: str,
    audit_status: str,
    translate_fn: TranslateFn,
) -> dict[str, str]:
    return {
        "profile": translate_fn(profile_display_i18n_key(profile_key)),
        "capabilities": format_profile_plain_summary(profile_key, translate_fn),
        "lifetime": translate_fn(f"profile.lifetime.{lifetime}"),
        "risk": translate_fn(f"cap.risk.{profile_risk_level(profile_key)}"),
        "audit": translate_fn(f"profile.audit.{audit_status}"),
    }


def build_safety_summary_body(
    *,
    profile_key: str,
    lifetime: str,
    translate_fn: TranslateFn,
) -> str:
    """Multi-section plain-language summary shown before applying a profile."""
    display = translate_fn(profile_display_i18n_key(profile_key))
    allowed = format_profile_plain_summary(profile_key, translate_fn)
    confirms = "\n".join(f"• {translate_fn(key)}" for key in STOP_ZONE_CONFIRM_I18N_KEYS)
    expires = translate_fn(f"profile.lifetime.{lifetime}")
    revoke = translate_fn("security.safety.revoke_hint")
    return translate_fn("security.safety.summary.body").format(
        profile=display,
        allowed=allowed,
        confirms=confirms,
        expires=expires,
        revoke=revoke,
    )


def build_full_assistance_session_spec(
    duration_minutes: int,
    *,
    workspace_path: str | None = None,
) -> dict[str, Any]:
    """Merged temporary session for everyday PC assistance."""
    minutes = int(duration_minutes)
    if minutes not in FULL_ASSISTANCE_DURATION_MINUTES:
        minutes = FULL_ASSISTANCE_DURATION_MINUTES[0]
    spec = ActionSessionStore.merge_plan_bound_session_spec(
        list(FULL_ASSISTANCE_PRESET_KEYS),
        workspace_path=workspace_path,
        permission_preset=FULL_ASSISTANCE_PROFILE_KEY,
    )
    spec["ttl_seconds"] = max(int(spec.get("ttl_seconds") or 0), minutes * 60)
    scope = dict(spec.get("scope") or {})
    scope["preset_key"] = "full_assistance"
    spec["scope"] = scope
    return spec


def nearest_session_remaining_seconds(active_sessions: list[dict[str, Any]]) -> int | None:
    """Seconds until the soonest-expiring active session, or None."""
    best: int | None = None
    now = dt.datetime.now(dt.timezone.utc)
    for session in active_sessions:
        if not isinstance(session, dict):
            continue
        expires_raw = str(session.get("expires_at") or "")
        try:
            expires_dt = dt.datetime.fromisoformat(expires_raw.replace("Z", "+00:00"))
            if expires_dt.tzinfo is None:
                expires_dt = expires_dt.replace(tzinfo=dt.timezone.utc)
            seconds = int((expires_dt - now).total_seconds())
            if best is None or seconds < best:
                best = seconds
        except Exception:
            continue
    return best


def format_remaining_duration(seconds: int | None, translate_fn: TranslateFn) -> str:
    if seconds is None:
        return translate_fn("security.status.no_active_sessions")
    if seconds <= 0:
        return translate_fn("cap.expires_in.expired")
    if seconds < 3600:
        return translate_fn("cap.expires_in.minutes").format(m=max(1, seconds // 60))
    return translate_fn("cap.expires_in.hours").format(h=max(1, seconds // 3600))


def short_device_id(device_id: str) -> str:
    """First UUID segment for compact display in status cards."""
    raw = str(device_id or "").strip()
    if not raw:
        return ""
    return raw.split("-", 1)[0]


def format_device_identity_label(
    *,
    device_name: str = "",
    hostname: str = "",
    device_id: str = "",
    translate_fn: TranslateFn,
) -> str:
    """Human-readable label for the device this console install represents."""
    name = str(device_name or "").strip()
    host = str(hostname or "").strip()
    short_id = short_device_id(device_id)
    primary = name or host
    if primary and short_id and primary != short_id:
        return translate_fn("security.status.this_device.named").format(
            name=primary,
            id=short_id,
        )
    if primary:
        return primary
    if short_id:
        return translate_fn("security.status.this_device.id_only").format(id=short_id)
    return translate_fn("security.status.this_device.unknown")


def build_security_status_snapshot(
    *,
    paired: bool,
    trust_state: str,
    profile_key: str,
    active_session_count: int,
    active_sessions: list[dict[str, Any]],
    translate_fn: TranslateFn,
    device_name: str = "",
    hostname: str = "",
    device_id: str = "",
) -> dict[str, str]:
    """Values for the Security tab status cards (scoped to this console install)."""
    trusted = str(trust_state or "").strip().lower() == "trusted"
    return {
        "this_device": format_device_identity_label(
            device_name=device_name,
            hostname=hostname,
            device_id=device_id,
            translate_fn=translate_fn,
        ),
        "connected": translate_fn("security.status.connected.yes" if paired else "security.status.connected.no"),
        "trusted_device": translate_fn(
            "security.status.trusted.yes" if trusted else "security.status.trusted.no"
        ),
        "current_permissions": translate_fn(profile_display_i18n_key(profile_key)),
        "active_sessions": str(active_session_count),
        "time_remaining": format_remaining_duration(
            nearest_session_remaining_seconds(active_sessions),
            translate_fn,
        ),
    }


def apply_permission_preset_selection(preset_key: str, *, blocked_keys: set[str] | None = None) -> dict[str, bool]:
    """Return checkbox state for each UI capability after choosing a profile preset."""
    preset = permission_preset_ui_keys(preset_key)
    blocked = blocked_keys or set()
    return {
        key: key in preset and key not in blocked
        for key in UI_CAPABILITY_KEYS
    }
