"""User-visible error categories (ERR01–ERR04) mapped from internal [Exxx] codes."""

from __future__ import annotations

import re
from enum import Enum
from typing import Any, Optional

_E_CODE_RE = re.compile(r"\[E\d{3}\]")


class UserErrorCategory(str, Enum):
    """Top-level categories shown in Electron error popups."""

    ERR01 = "ERR01"  # Unable to start
    ERR02 = "ERR02"  # Port busy
    ERR03 = "ERR03"  # Timezone / time sync
    ERR04 = "ERR04"  # User deleted / unauthorised


_USER_CATEGORY_TITLES = {
    UserErrorCategory.ERR01: "Unable to start",
    UserErrorCategory.ERR02: "Port Busy",
    UserErrorCategory.ERR03: "Timezone",
    UserErrorCategory.ERR04: "User deleted",
}

_SUBCATEGORY_LABELS = {
    "antivirus_shell_missing": "Antivirus — shell startup missing",
    "antivirus_files_deleted": "Antivirus — files deleted from home directory",
    "python_thread_kills": "Python process killed (thread failure)",
    "database_invalid": "File is not a database",
    "generic": "Startup failure",
    "port_busy_installation": "Installation — required port is already in use",
    "server_429": "Server didn't respond (rate limit)",
    "time_mismatch_sleep": "Time mismatch in sleep/wake cases",
    "timezone_mismatch": "Timezone mismatch",
    "clock_adjustment": "System clock adjustment detected",
    "not_authorised": "Not authorised",
    "user_deleted_deactivated": "Account deleted or deactivated",
    "subscription_expired": "Subscription expired",
}

_E_CODE_MAP: dict[str, tuple[UserErrorCategory, str]] = {
    "[E001]": (UserErrorCategory.ERR02, "port_busy_installation"),
    "[E020]": (UserErrorCategory.ERR01, "antivirus_shell_missing"),
    "[E030]": (UserErrorCategory.ERR01, "antivirus_shell_missing"),
    "[E040]": (UserErrorCategory.ERR01, "antivirus_shell_missing"),
    "[E043]": (UserErrorCategory.ERR01, "antivirus_shell_missing"),
    "[E100]": (UserErrorCategory.ERR01, "antivirus_shell_missing"),
    "[E108]": (UserErrorCategory.ERR01, "antivirus_shell_missing"),
    "[E031]": (UserErrorCategory.ERR01, "antivirus_files_deleted"),
    "[E032]": (UserErrorCategory.ERR01, "antivirus_files_deleted"),
    "[E041]": (UserErrorCategory.ERR01, "antivirus_files_deleted"),
    "[E042]": (UserErrorCategory.ERR01, "antivirus_files_deleted"),
    "[E054]": (UserErrorCategory.ERR01, "antivirus_files_deleted"),
    "[E080]": (UserErrorCategory.ERR01, "python_thread_kills"),
    "[E081]": (UserErrorCategory.ERR01, "python_thread_kills"),
    "[E082]": (UserErrorCategory.ERR01, "python_thread_kills"),
    "[E084]": (UserErrorCategory.ERR01, "python_thread_kills"),
    "[E085]": (UserErrorCategory.ERR01, "python_thread_kills"),
    "[E060]": (UserErrorCategory.ERR01, "database_invalid"),
    "[E061]": (UserErrorCategory.ERR01, "database_invalid"),
    "[E062]": (UserErrorCategory.ERR01, "database_invalid"),
    "[E105]": (UserErrorCategory.ERR02, "port_busy_installation"),
    "[E106]": (UserErrorCategory.ERR02, "port_busy_installation"),
    "[E110]": (UserErrorCategory.ERR02, "port_busy_installation"),
    "[E050]": (UserErrorCategory.ERR03, "timezone_mismatch"),
    "[E074]": (UserErrorCategory.ERR03, "time_mismatch_sleep"),
    "[E093]": (UserErrorCategory.ERR03, "server_429"),
    "[E051]": (UserErrorCategory.ERR04, "not_authorised"),
    "[E052]": (UserErrorCategory.ERR04, "not_authorised"),
    "[E053]": (UserErrorCategory.ERR04, "not_authorised"),
    "[E000]": (UserErrorCategory.ERR01, "generic"),
}


def extract_e_code_prefix(code: str) -> Optional[str]:
    if not code:
        return None
    match = _E_CODE_RE.search(str(code))
    return match.group(0) if match else None


def resolve_subscription_subcategory(response: Optional[dict]) -> str:
    if not response:
        return "user_deleted_deactivated"
    is_deleted = response.get("isdeleted") in (1, True) or response.get("is_deleted") in (1, True)
    is_deactivated = response.get("isdeactivated") in (1, True) or response.get("is_deactivated") in (1, True)
    if is_deleted or is_deactivated:
        return "user_deleted_deactivated"
    plan_status = response.get("planStatus") or response.get("plan_status")
    if plan_status and str(plan_status).lower() != "active":
        return "subscription_expired"
    return "user_deleted_deactivated"


def resolve_user_error_from_code(code: str) -> dict[str, str]:
    """Map an ErrorCode value string to user category fields for Electron."""
    prefix = extract_e_code_prefix(str(code))
    if prefix and prefix in _E_CODE_MAP:
        category, subcategory = _E_CODE_MAP[prefix]
        return {
            "userCategory": category.value,
            "userCategoryTitle": _USER_CATEGORY_TITLES[category],
            "subcategory": subcategory,
            "subcategoryLabel": _SUBCATEGORY_LABELS.get(subcategory, ""),
        }
    return {
        "userCategory": UserErrorCategory.ERR01.value,
        "userCategoryTitle": _USER_CATEGORY_TITLES[UserErrorCategory.ERR01],
        "subcategory": "generic",
        "subcategoryLabel": _SUBCATEGORY_LABELS["generic"],
    }


def enrich_electron_payload(code: str, payload: Optional[dict[str, Any]] = None) -> dict[str, Any]:
    """Merge user category fields into an IPC payload dict."""
    result = dict(payload or {})
    result.update(resolve_user_error_from_code(code))
    return result


def enrich_subscription_payload(response: Optional[dict] = None) -> dict[str, str]:
    subcategory = resolve_subscription_subcategory(response)
    return {
        "userCategory": UserErrorCategory.ERR04.value,
        "userCategoryTitle": _USER_CATEGORY_TITLES[UserErrorCategory.ERR04],
        "subcategory": subcategory,
        "subcategoryLabel": _SUBCATEGORY_LABELS.get(subcategory, ""),
    }
