"""
IPC handshake authentication (Phase A) using a predefined shared secret.

Secret resolution order (first match wins):
  1. WORKSTATUS_IPC_SECRET environment variable
  2. security.ini [Security] ipc_shared_secret
  3. security.ini [Security] shared_secret

Legacy handshakes (no MAC) are still accepted when no secret is configured.
"""

from __future__ import annotations

import hashlib
import hmac
import os
import secrets
import sys
import time
import configparser
from pathlib import Path
from typing import Any, Dict, Optional, Tuple

from ws_package.log.writer import LogWriter

logger = LogWriter.file(__name__)

IPC_HMAC_LABEL = "workstatus-ipc-v1"

_secret: Optional[bytes] = None


def get_ipc_data_dir() -> Path:
    """Same directory as Electron's workstatus-desktop.sqlite."""
    home = Path.home()
    system = __import__("platform").system()
    if system == "Darwin":
        return home / "Library/Application Support/Workstatus"
    if system == "Linux":
        return home / "Workstatus"
    appdata = os.environ.get("APPDATA") or str(home / "AppData" / "Roaming")
    return Path(appdata) / "Workstatus"


def _derive_key_from_config_value(raw: str) -> bytes:
    """Derive stable 32-byte IPC HMAC key from ini/env string."""
    material = raw.strip().encode("utf-8")
    return hmac.new(material, IPC_HMAC_LABEL.encode("utf-8"), hashlib.sha256).digest()


def _parse_security_ini(path: Path) -> Optional[str]:
    try:
        if not path.is_file():
            return None
        parser = configparser.ConfigParser()
        parser.read(path, encoding="utf-8")
        if parser.has_option("Security", "ipc_shared_secret"):
            val = parser.get("Security", "ipc_shared_secret").strip()
            if val:
                return val
        if parser.has_option("Security", "shared_secret"):
            val = parser.get("Security", "shared_secret").strip()
            if val:
                return val
    except Exception as exc:
        logger.debug("Could not parse %s: %s", path, exc)
    return None


def _security_ini_search_paths() -> list:
    paths: list = []
    env_ini = os.environ.get("WORKSTATUS_SECURITY_INI", "").strip()
    if env_ini:
        paths.append(Path(env_ini))
    paths.append(get_ipc_data_dir() / "security.ini")
    if getattr(sys, "frozen", False):
        exe_dir = Path(sys.executable).resolve().parent
        paths.append(exe_dir / "security.ini")
        paths.append(exe_dir / "_internal" / "ws_package" / "security.ini")
    else:
        # Development: ws_package/security.ini next to source tree
        pkg_ini = Path(__file__).resolve().parent.parent / "security.ini"
        paths.append(pkg_ini)
    try:
        from importlib.resources import files, as_file

        with as_file(files("ws_package").joinpath("security.ini")) as bundled:
            paths.append(Path(bundled))
    except Exception:
        pass
    return paths


def _load_secret_from_security_ini() -> Optional[str]:
    seen = set()
    for path in _security_ini_search_paths():
        key = str(path)
        if key in seen:
            continue
        seen.add(key)
        value = _parse_security_ini(path)
        if value:
            logger.debug("IPC secret loaded from %s", path)
            return value
    return None


def _resolve_config_string() -> Optional[str]:
    for env_name in ("WORKSTATUS_IPC_SECRET", "WORKSTATUS_IPC_SHARED_SECRET"):
        env_val = os.environ.get(env_name, "").strip()
        if env_val:
            return env_val
    return _load_secret_from_security_ini()


def init_ipc_session() -> bool:
    """Load predefined IPC HMAC key into memory (no files written)."""
    global _secret
    if _secret is not None:
        return True
    raw = _resolve_config_string()
    if not raw:
        logger.warning("IPC auth: no secret in env or security.ini — legacy handshake only")
        return False
    _secret = _derive_key_from_config_value(raw)
    logger.info("IPC auth: using predefined shared secret (env or security.ini)")
    return True


def cleanup_ipc_session() -> None:
    """Clear in-memory secret (optional on shutdown)."""
    global _secret
    _secret = None


def get_session_secret() -> Optional[bytes]:
    return _secret


def _compute_mac(secret: bytes, side: str, nonce: str) -> str:
    return hmac.new(secret, f"{side}:{nonce}".encode("utf-8"), hashlib.sha256).hexdigest()


def verify_electron_handshake(message: Dict[str, Any]) -> Tuple[bool, bool]:
    """
    Validate hello_from_electron.

    Returns:
        (accepted, secure_mode)
    """
    if _secret is None:
        return True, False

    data = message.get("data") or {}
    client_mac = data.get("mac")
    client_nonce = data.get("nonce")

    if not client_mac:
        logger.debug("IPC handshake: legacy client (no MAC)")
        return True, False

    if not client_nonce or not isinstance(client_nonce, str):
        logger.warning("IPC handshake rejected: MAC without nonce")
        return False, False

    expected = _compute_mac(_secret, "electron", client_nonce)
    if not hmac.compare_digest(expected, str(client_mac)):
        logger.warning("IPC handshake rejected: invalid client MAC")
        return False, False

    logger.info("IPC handshake: client MAC verified")
    return True, True


def build_python_handshake_data() -> Optional[Dict[str, str]]:
    if _secret is None:
        return None
    nonce = secrets.token_hex(16)
    return {
        "nonce": nonce,
        "mac": _compute_mac(_secret, "python", nonce),
        "auth": "hmac-sha256",
    }


def make_python_handshake_response(pipe_session_id: str, secure: bool = False) -> Dict[str, Any]:
    response: Dict[str, Any] = {
        "type": "hello_from_python",
        "timestamp": time.time(),
        "session_id": pipe_session_id,
    }
    if secure:
        py_data = build_python_handshake_data()
        if py_data:
            response["data"] = py_data
    return response
