"""Application-wide exit codes, error identifiers, and structured error types.

Every sys.exit() and os._exit() in the codebase uses a code from ExitCode.
Every structured log on an error path includes an ErrorCode tag so support
can grep a single string to find all occurrences of a specific failure.

Structured errors
-----------------
``AppError`` wraps an ``ErrorCode`` with a human-readable message, optional
recovery hint, and an ``is_recoverable`` flag.  Raise ``AppError`` instead of
bare exceptions on error paths so callers can distinguish recoverable from
fatal failures without parsing strings::

    raise AppError(
        ErrorCode.NETWORK_TIMEOUT,
        "POST /api/activity timed out after 30 s",
        is_recoverable=True,
    )

Exit code conventions
---------------------
0   Normal / intentional hand-off (launching another process then quitting).
1   Fatal startup failure — wrong files, bad environment, upgrade in progress.
2   Runtime force-exit — timezone mismatch, critical auth failure.
3   Graceful stop — user-initiated clean shutdown.

Support workflow
----------------
1. Grep the log for  [E0xx]  to jump straight to the failure.
2. Cross-reference the code below for description + remediation hints.

User-visible error categories (sent to Electron as "tracking_error" messages)
------------------------------------------------------------------------------
Codes E070–E083 and E090–E115 may be surfaced to the user in the UI.
The "code" field in the Electron message matches the [Exxx] tag so the
front-end can display a human-readable explanation per error type.
"""

from __future__ import annotations

from enum import Enum
from typing import Optional


# ---------------------------------------------------------------------------
# Structured error type
# ---------------------------------------------------------------------------

class AppError(Exception):
    """Structured application error that carries an ErrorCode.

    Preferred over bare exceptions on every error path so callers can:
    * Log a consistent ``[Exxx]`` tag without string parsing.
    * Branch on ``is_recoverable`` to decide between retry and abort.
    * Surface ``recovery_hint`` to the Electron UI for user action.

    Args:
        code: The ``ErrorCode`` value for this failure.
        message: Human-readable description (goes to log + Electron UI).
        is_recoverable: True if the app can retry / self-heal.  False means
            user or operator action is required.
        recovery_hint: Optional one-sentence instruction for the user.
        context: Optional dict of diagnostic key/value pairs included in logs.
    """

    def __init__(
        self,
        code: "ErrorCode",
        message: str,
        *,
        is_recoverable: bool = True,
        recovery_hint: Optional[str] = None,
        context: Optional[dict] = None,
    ) -> None:
        super().__init__(message)
        self.code = code
        self.message = message
        self.is_recoverable = is_recoverable
        self.recovery_hint = recovery_hint
        self.context: dict = context or {}

    def to_electron_payload(self) -> dict:
        """Serialize for the ``tracking_error`` IPC message sent to Electron."""
        from ws_package.utils.user_error_categories import enrich_electron_payload

        payload = {
            "code": self.code.value,
            "message": self.message,
            "recoverable": self.is_recoverable,
        }
        if self.recovery_hint:
            payload["hint"] = self.recovery_hint
        return enrich_electron_payload(self.code.value, payload)

    def __str__(self) -> str:
        return f"{self.code.value}: {self.message}"


class ExitCode(int, Enum):
    """Integer exit codes passed to sys.exit() / os._exit()."""

    OK = 0
    """Normal exit: setup complete, handing off to another process."""

    STARTUP_FAILURE = 1
    """Fatal startup problem: missing file, bad environment, upgrade pending."""

    RUNTIME_FORCE = 2
    """Runtime force-exit: timezone mismatch, critical auth, unrecoverable state."""

    GRACEFUL_STOP = 3
    """Requested graceful stop: user-initiated shutdown, clean exit."""


class ErrorCode(str, Enum):
    """
    Short tags embedded in every structured log entry on an error path.

    Format: [Exxx] — grep-friendly so support can filter instantly.

    Instance management
    -------------------
    E001  Another instance already running.

    Upgrade
    -------
    E010  Linux upgrade handoff — launching update_helper.sh and exiting.
    E011  macOS upgrade handoff — launching update_helper.sh and exiting.
    E012  Windows upgrade handoff — launching upgrade_helper.bat and exiting.

    Windows first-run setup
    -----------------------
    E020  Windows setup: required executable not found (onedir build missing _internal/).

    Linux first-run setup
    ---------------------
    E030  Linux setup: required executable not found.
    E031  Linux setup: required token file not found.
    E032  Linux setup: file copy to ~/Workstatus failed.
    E033  Linux setup: launching ~/Workstatus version and exiting.

    macOS first-run setup
    ---------------------
    E040  macOS setup: required executable not found.
    E041  macOS setup: required token file not found.
    E042  macOS setup: file copy to Application Support failed.
    E043  macOS setup: all launch methods failed, falling back to desktop version.
    E044  macOS setup: launching Application Support version and exiting desktop copy.

    Authentication / user
    ---------------------
    E050  Timezone mismatch between local clock and org — notified server, exiting.
    E051  Critical auth failure — token invalid or revoked, cannot continue.
    E052  Login failed — server returned non-200, entering fallback mode.
    E053  Token decryption failed.
    E054  Token file missing on disk.

    Database
    --------
    E060  Database corruption detected — running repair.
    E061  Database repair failed — manual intervention required.
    E062  Database lock timeout — operation aborted.

    Screenshot
    ----------
    E070  Screenshot capture failed — permission or display server error.
            Remediation: on macOS grant Screen Recording in System Settings →
            Privacy & Security; on Windows ensure the app is not blocked by UAC/UIPI.
    E071  Screenshot S3 upload failed — network or credentials error.
            Remediation: check internet connection and S3 bucket configuration.
    E072  Screenshot file not found after capture.
    E073  Screenshot save to database failed.
    E074  S3 time-tampering detected — system clock out of sync with AWS.
            Remediation: sync system clock (NTP) and ensure no time-manipulation software.
    E075  Screenshot permission denied — OS security policy blocking capture.
            Remediation: grant Screen Recording (macOS) or disable UIPI override (Windows).
    E076  Screenshot disk write failed — disk full or path permission denied.
            Remediation: free disk space or check write permissions on screenshot directory.

    Activity monitoring
    -------------------
    E080  Device listener thread died and could not be restarted.
            Remediation: on macOS grant Accessibility in System Settings → Privacy & Security.
    E081  Push activity loop: unrecoverable error.
    E082  Status activity loop: unrecoverable error.
    E083  Activity percentage calculation failed.
    E084  Keyboard/mouse listener failed to start — accessibility permission denied.
            Remediation: grant Accessibility access in System Settings → Privacy & Security.
    E085  Activity monitoring thread crashed and restart failed.
            Remediation: restart the app; if recurring, check antivirus exclusions.

    Network / API
    -------------
    E090  Network unreachable — request failed after retries.
            Remediation: check internet connection; verify firewall/proxy settings.
    E091  API returned unexpected status code.
    E092  S3 configuration missing or invalid.
    E093  API rate limit exceeded — server is throttling requests (HTTP 429).
            Remediation: this resolves automatically; if persistent contact support.
    E094  S3 credentials expired or invalid — AWS access denied.
            Remediation: log out and log back in to refresh credentials.
    E095  Network completely offline — all connectivity lost.
            Remediation: restore internet connection; tracker will auto-resume.

    Resources / environment
    -----------------------
    E100  PyInstaller resource recovery failed.
    E101  AWS services unhealthy and recovery failed.
    E102  SSL configuration for boto3 failed.
    E104  System wake detector failed to initialise.
    E105  IPC server failed to start — Electron communication unavailable.
            Remediation: check if port 54321 (macOS/Linux) is blocked by firewall or
            antivirus; on Windows check Named Pipe access rights.
    E106  IPC port already in use — another process is holding port 54321.
            Remediation: kill the stale process (taskkill /F /PID on Windows, kill on
            macOS/Linux) or restart the machine; antivirus may also block port binding.
    E107  WebSocket connection to real-time server failed.
            Remediation: check internet connection and firewall rules.
    E108  Antivirus or firewall blocked a required operation.
            Remediation: add Workstatus to your antivirus/firewall exclusion list.
    E109  Disk full — cannot write screenshot or database record.
            Remediation: free disk space; tracker resumes automatically when space available.
    E110  IPC pipe creation failed on Windows (Named Pipe unavailable).
            Remediation: check if another instance is running; antivirus may block pipe creation.

    Unhandled / catch-all
    ----------------------
    E000  Unhandled exception at top-level — process exiting with unknown error.
    """

    # Instance management
    ALREADY_RUNNING = "[E001] Another instance is already running"

    # Upgrade
    UPGRADE_LINUX = "[E010] Linux upgrade handoff"
    UPGRADE_MACOS = "[E011] macOS upgrade handoff"
    UPGRADE_WINDOWS = "[E012] Windows upgrade handoff"

    # Windows setup
    SETUP_WIN_MISSING_EXE = "[E020] Windows setup: executable not found"

    # Linux setup
    SETUP_LIN_MISSING_EXE = "[E030] Linux setup: executable not found"
    SETUP_LIN_MISSING_TOKEN = "[E031] Linux setup: token file not found"
    SETUP_LIN_COPY_FAILED = "[E032] Linux setup: file copy failed"
    SETUP_LIN_HANDOFF = "[E033] Linux setup: handing off to ~/Workstatus version"

    # macOS setup
    SETUP_MAC_MISSING_EXE = "[E040] macOS setup: executable not found"
    SETUP_MAC_MISSING_TOKEN = "[E041] macOS setup: token file not found"
    SETUP_MAC_COPY_FAILED = "[E042] macOS setup: file copy failed"
    SETUP_MAC_LAUNCH_FAILED = "[E043] macOS setup: all launch methods failed"
    SETUP_MAC_HANDOFF = "[E044] macOS setup: handing off to Application Support version"

    # Auth / user
    TIMEZONE_MISMATCH = "[E050] Timezone mismatch — local clock differs from org timezone"
    AUTH_CRITICAL = "[E051] Critical auth failure — token invalid or revoked"
    LOGIN_FAILED = "[E052] Login failed — server non-200, entering fallback mode"
    TOKEN_DECRYPT_FAILED = "[E053] Token decryption failed"
    TOKEN_FILE_MISSING = "[E054] Token file not found on disk"

    # Database
    DB_CORRUPTION = "[E060] Database corruption detected"
    DB_REPAIR_FAILED = "[E061] Database repair failed"
    DB_LOCK_TIMEOUT = "[E062] Database lock timeout"

    # Screenshots
    SS_CAPTURE_FAILED = "[E070] Screenshot capture failed"
    SS_UPLOAD_FAILED = "[E071] Screenshot S3 upload failed"
    SS_FILE_MISSING = "[E072] Screenshot file not found after capture"
    SS_DB_SAVE_FAILED = "[E073] Screenshot save to database failed"
    SS_TIME_TAMPER = "[E074] S3 time-tampering detected"
    SS_PERMISSION_DENIED = "[E075] Screenshot permission denied by OS"
    SS_DISK_WRITE_FAILED = "[E076] Screenshot disk write failed — disk full or permissions"

    # Activity
    DEVICE_LISTENER_DEAD = "[E080] Device listener thread could not be restarted"
    PUSH_LOOP_FAILED = "[E081] Push activity loop unrecoverable error"
    STATUS_LOOP_FAILED = "[E082] Status activity loop unrecoverable error"
    ACTIVITY_PCT_FAILED = "[E083] Activity percentage calculation failed"
    INPUT_LISTENER_FAILED = "[E084] Keyboard/mouse listener failed — accessibility permission denied"
    ACTIVITY_THREAD_CRASHED = "[E085] Activity monitoring thread crashed and restart failed"

    # Network / API
    NETWORK_UNREACHABLE = "[E090] Network unreachable after retries"
    API_UNEXPECTED_STATUS = "[E091] API unexpected status code"
    S3_CONFIG_MISSING = "[E092] S3 configuration missing or invalid"
    API_RATE_LIMITED = "[E093] API rate limit exceeded (HTTP 429)"
    S3_CREDENTIALS_EXPIRED = "[E094] S3/AWS credentials expired or invalid"
    NETWORK_OFFLINE = "[E095] Network completely offline — all connectivity lost"

    # Resources / IPC
    RESOURCE_RECOVERY_FAILED = "[E100] PyInstaller resource recovery failed"
    AWS_UNHEALTHY = "[E101] AWS services unhealthy and recovery failed"
    SSL_SETUP_FAILED = "[E102] SSL configuration for boto3 failed"
    WAKE_DETECTOR_FAILED = "[E104] System wake detector failed to initialise"
    IPC_SERVER_FAILED = "[E105] IPC server failed to start"
    IPC_PORT_IN_USE = "[E106] IPC port already in use — another process holds port 54321"
    WEBSOCKET_FAILED = "[E107] WebSocket connection to real-time server failed"
    ANTIVIRUS_BLOCKED = "[E108] Antivirus or firewall blocked a required operation"
    DISK_FULL = "[E109] Disk full — cannot write screenshot or database record"
    IPC_PIPE_FAILED = "[E110] IPC Named Pipe creation failed on Windows"

    # Network resilience (E120–E129)
    # --------------------------------
    # E120  Request timed out (connect or read timeout).
    # E121  DNS resolution failure — hostname could not be resolved.
    # E122  Slow internet detected — response time exceeded quality threshold.
    # E123  Retry budget exhausted — all retry attempts failed.
    # E124  Circuit breaker open — endpoint temporarily disabled after repeated failures.
    # E125  SSL/TLS handshake failed — certificate or protocol error.
    # E126  Proxy or firewall intercepting HTTPS — transparent proxy detected.
    # E127  Server returned redirect loop — request not completing.
    # E128  Response body too large — payload exceeds safety limit.
    # E129  Request cancelled — caller requested cancellation before response.

    NETWORK_TIMEOUT = "[E120] Request timed out (connect or read timeout)"
    DNS_FAILURE = "[E121] DNS resolution failed — hostname unresolvable"
    SLOW_INTERNET = "[E122] Slow internet detected — response time exceeded threshold"
    RETRY_EXHAUSTED = "[E123] All retry attempts failed — giving up"
    CIRCUIT_BREAKER_OPEN = "[E124] Circuit breaker open — endpoint temporarily disabled"
    SSL_HANDSHAKE_FAILED = "[E125] SSL/TLS handshake failed"
    PROXY_INTERCEPT = "[E126] Transparent proxy or firewall intercepting HTTPS"
    REDIRECT_LOOP = "[E127] Server returned a redirect loop"
    RESPONSE_TOO_LARGE = "[E128] Response body exceeds safety limit"
    REQUEST_CANCELLED = "[E129] Request cancelled by caller"

    # Auth lifecycle (E130–E139)
    # --------------------------
    # E130  Authentication state machine entered an unexpected state.
    # E131  Deferred message queue overflowed — message dropped.
    # E132  IPC client disconnected during authentication.
    # E133  Authentication timed out waiting for user credentials.

    AUTH_STATE_ERROR = "[E130] Auth state machine entered unexpected state"
    DEFERRED_QUEUE_OVERFLOW = "[E131] Deferred message queue overflow — message dropped"
    IPC_DISCONNECTED_DURING_AUTH = "[E132] IPC client disconnected during authentication"
    AUTH_TIMEOUT = "[E133] Authentication timed out waiting for credentials"

    # Catch-all
    UNHANDLED_EXCEPTION = "[E000] Unhandled top-level exception"
