from __future__ import annotations

import json
import subprocess
import sys
from collections.abc import Callable
from pathlib import Path
from typing import Any, Literal, TypedDict


class HostWifiProfileAssociationResult(TypedDict):
    schema_version: int
    adapter: str
    outcome: str
    already_associated: bool
    profile_enrolled: bool
    scan_attempt_count: int
    scan_elapsed_ms: int
    credential_source: str


class HostWifiProfileStoreResult(TypedDict):
    schema_version: int
    adapter: str
    outcome: Literal["stored"]


class HostWifiProfileAvailabilityResult(TypedDict):
    schema_version: int
    adapter: str
    available: bool


class HostWifiProfileEnsureResult(TypedDict):
    schema_version: int
    adapter: str
    available: bool
    profile_enrolled: bool
    credential_source: str | None


class HostWifiCredentialMaterialStoreResult(TypedDict):
    schema_version: int
    adapter: str
    outcome: Literal["stored"]


class HostWifiCredentialMaterialAvailabilityResult(TypedDict):
    schema_version: int
    adapter: str
    available: bool


_ERROR_MESSAGES = {
    "unsupported-platform": (
        "для этой ОС ещё не установлен адаптер системных Wi-Fi-профилей"
    ),
    "profile-unavailable": (
        "реквизиты выбранного K1 не заведены на этом управляющем устройстве"
    ),
    "credential-source-unavailable": (
        "credential provider точной версии прошивки не установлен на этом устройстве"
    ),
    "network-not-found": (
        "точка доступа выбранного K1 не найдена; проверьте режим K1 и питание"
    ),
    "profile-ssid-mismatch": "сохранённый профиль принадлежит другой точке доступа",
    "credential-entry-cancelled": "ввод пароля точки доступа K1 отменён оператором",
    "credential-invalid": "пароль точки доступа K1 имеет недопустимую длину",
    "host-wifi-operation-timeout": (
        "оператор не завершил системное подключение Wi-Fi за отведённое время"
    ),
    "wifi-interface-unavailable": "системный Wi-Fi-интерфейс недоступен",
    "corewlan-error": "системный Wi-Fi не смог подключиться к точке доступа K1",
}


class HostWifiProfileError(RuntimeError):
    """One bounded host-side Wi-Fi profile operation failed."""

    def __init__(
        self,
        reason_code: str,
        *,
        scan_attempt_count: int | None = None,
        scan_elapsed_ms: int | None = None,
    ) -> None:
        self.reason_code = reason_code
        self.scan_attempt_count = scan_attempt_count
        self.scan_elapsed_ms = scan_elapsed_ms
        message = _ERROR_MESSAGES.get(reason_code, "операция системного Wi-Fi завершилась ошибкой")
        super().__init__(f"{message} ({reason_code})")


RunProcess = Callable[..., subprocess.CompletedProcess[bytes]]


def _validate_profile_id(profile_id: str) -> None:
    if not 1 <= len(profile_id) <= 128:
        raise ValueError("host Wi-Fi profile id must contain between 1 and 128 characters")
    if not all(
        character.isascii() and (character.isalnum() or character in "._-")
        for character in profile_id
    ):
        raise ValueError("host Wi-Fi profile id contains unsupported characters")


def _run_macos_helper(
    helper_path: Path,
    request: dict[str, object],
    *,
    timeout_seconds: float,
    runner: RunProcess,
) -> dict[str, Any]:
    if sys.platform != "darwin":
        raise HostWifiProfileError("unsupported-platform")
    if not helper_path.is_file():
        raise HostWifiProfileError("host-wifi-helper-missing")
    if timeout_seconds <= 0:
        raise ValueError("timeout_seconds must be positive")

    request_bytes = bytearray(
        json.dumps(request, ensure_ascii=False, separators=(",", ":")).encode("utf-8")
    )
    try:
        completed = runner(
            ["/usr/bin/xcrun", "swift", str(helper_path.resolve())],
            input=request_bytes,
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE,
            timeout=timeout_seconds,
            check=False,
        )
    except subprocess.TimeoutExpired as exc:
        raise HostWifiProfileError("host-wifi-operation-timeout") from exc
    except OSError as exc:
        raise HostWifiProfileError("host-wifi-helper-unavailable") from exc
    finally:
        request_bytes[:] = b"\x00" * len(request_bytes)

    if len(completed.stdout) > 4096:
        raise HostWifiProfileError("host-wifi-response-too-large")
    try:
        response: Any = json.loads(completed.stdout.decode("utf-8"))
    except (UnicodeDecodeError, json.JSONDecodeError) as exc:
        raise HostWifiProfileError("host-wifi-response-invalid") from exc
    if not isinstance(response, dict):
        raise HostWifiProfileError("host-wifi-response-invalid")

    reason_code = response.get("reason_code")
    if completed.returncode != 0 or response.get("ok") is not True:
        if not isinstance(reason_code, str) or not reason_code:
            reason_code = "host-wifi-operation-failed"
        scan_attempt_count = response.get("scan_attempt_count")
        scan_elapsed_ms = response.get("scan_elapsed_ms")
        raise HostWifiProfileError(
            reason_code,
            scan_attempt_count=(
                scan_attempt_count
                if type(scan_attempt_count) is int and scan_attempt_count >= 1
                else None
            ),
            scan_elapsed_ms=(
                scan_elapsed_ms
                if type(scan_elapsed_ms) is int and scan_elapsed_ms >= 0
                else None
            ),
        )
    return response


def associate_with_wifi_profile_once(
    helper_path: Path,
    profile_id: str,
    expected_ssid: str,
    *,
    scan_timeout_seconds: float = 15.0,
    timeout_seconds: float = 180.0,
    runner: RunProcess = subprocess.run,
) -> HostWifiProfileAssociationResult:
    """Associate through one device-scoped OS profile without exposing its secret.

    The selected device supplies the expected, operator-visible AP SSID.  The
    platform helper performs bounded exact-SSID discovery followed by at most
    one association. It resolves a device-scoped secret from the OS credential
    stores, or asks for it through a native secure prompt on first use. The
    secret never crosses the helper boundary.
    """

    _validate_profile_id(profile_id)
    if not 1 <= len(expected_ssid.encode("utf-8")) <= 32:
        raise ValueError("SSID must contain between 1 and 32 UTF-8 bytes")
    if not 0 <= scan_timeout_seconds <= 60:
        raise ValueError("scan_timeout_seconds must be between 0 and 60")
    response = _run_macos_helper(
        helper_path,
        {
            "action": "associate",
            "profile_id": profile_id,
            "ssid": expected_ssid,
            "scan_timeout_seconds": scan_timeout_seconds,
        },
        timeout_seconds=timeout_seconds,
        runner=runner,
    )
    already_associated = response.get("already_associated")
    profile_enrolled = response.get("profile_enrolled")
    scan_attempt_count = response.get("scan_attempt_count")
    scan_elapsed_ms = response.get("scan_elapsed_ms")
    credential_source = response.get("credential_source")
    adapter = response.get("adapter")
    if (
        not isinstance(already_associated, bool)
        or not isinstance(profile_enrolled, bool)
        or not isinstance(adapter, str)
        or not adapter
        or type(scan_attempt_count) is not int
        or scan_attempt_count < 1
        or type(scan_elapsed_ms) is not int
        or scan_elapsed_ms < 0
        or not isinstance(credential_source, str)
        or credential_source not in {
            "mission-core-keychain",
            "system-wifi-keychain",
            "native-secure-prompt",
            "exact-firmware-profile",
        }
    ):
        raise HostWifiProfileError("host-wifi-response-invalid")
    return {
        "schema_version": 1,
        "adapter": adapter,
        "outcome": "already-associated" if already_associated else "associated",
        "already_associated": already_associated,
        "profile_enrolled": profile_enrolled,
        "scan_attempt_count": scan_attempt_count,
        "scan_elapsed_ms": scan_elapsed_ms,
        "credential_source": credential_source,
    }


def check_wifi_profile(
    helper_path: Path,
    profile_id: str,
    expected_ssid: str,
    *,
    timeout_seconds: float = 30.0,
    runner: RunProcess = subprocess.run,
) -> HostWifiProfileAvailabilityResult:
    """Check one device-scoped Keychain profile without scanning or joining Wi-Fi."""

    _validate_profile_id(profile_id)
    if not 1 <= len(expected_ssid.encode("utf-8")) <= 32:
        raise ValueError("SSID must contain between 1 and 32 UTF-8 bytes")
    response = _run_macos_helper(
        helper_path,
        {
            "action": "check-profile",
            "profile_id": profile_id,
            "ssid": expected_ssid,
        },
        timeout_seconds=timeout_seconds,
        runner=runner,
    )
    available = response.get("profile_available")
    adapter = response.get("adapter")
    if not isinstance(available, bool) or not isinstance(adapter, str) or not adapter:
        raise HostWifiProfileError("host-wifi-response-invalid")
    return {
        "schema_version": 1,
        "adapter": adapter,
        "available": available,
    }


def ensure_wifi_profile_from_credential_source(
    helper_path: Path,
    profile_id: str,
    expected_ssid: str,
    credential_source_id: str,
    *,
    timeout_seconds: float = 30.0,
    runner: RunProcess = subprocess.run,
) -> HostWifiProfileEnsureResult:
    """Materialize one device profile from a firmware-scoped secure-store item.

    Only opaque identifiers and the operator-visible SSID cross the platform
    helper boundary. The credential stays inside the OS credential store.
    """

    _validate_profile_id(profile_id)
    _validate_profile_id(credential_source_id)
    if not 1 <= len(expected_ssid.encode("utf-8")) <= 32:
        raise ValueError("SSID must contain between 1 and 32 UTF-8 bytes")
    response = _run_macos_helper(
        helper_path,
        {
            "action": "ensure-profile",
            "profile_id": profile_id,
            "ssid": expected_ssid,
            "credential_source_id": credential_source_id,
        },
        timeout_seconds=timeout_seconds,
        runner=runner,
    )
    available = response.get("profile_available")
    profile_enrolled = response.get("profile_enrolled")
    credential_source = response.get("credential_source")
    adapter = response.get("adapter")
    if (
        not isinstance(available, bool)
        or not isinstance(profile_enrolled, bool)
        or not isinstance(adapter, str)
        or not adapter
        or (credential_source is not None and not isinstance(credential_source, str))
    ):
        raise HostWifiProfileError("host-wifi-response-invalid")
    return {
        "schema_version": 1,
        "adapter": adapter,
        "available": available,
        "profile_enrolled": profile_enrolled,
        "credential_source": credential_source,
    }


def store_wifi_credential_material(
    helper_path: Path,
    credential_source_id: str,
    password: str,
    *,
    timeout_seconds: float = 30.0,
    runner: RunProcess = subprocess.run,
) -> HostWifiCredentialMaterialStoreResult:
    """Store firmware-scoped material without putting it in argv or output."""

    _validate_profile_id(credential_source_id)
    if not 8 <= len(password.encode("utf-8")) <= 63:
        raise ValueError("WPA-PSK must contain between 8 and 63 UTF-8 bytes")
    response = _run_macos_helper(
        helper_path,
        {
            "action": "store-credential-material",
            "profile_id": credential_source_id,
            "password": password,
        },
        timeout_seconds=timeout_seconds,
        runner=runner,
    )
    adapter = response.get("adapter")
    if response.get("stored") is not True or not isinstance(adapter, str) or not adapter:
        raise HostWifiProfileError("host-wifi-response-invalid")
    return {
        "schema_version": 1,
        "adapter": adapter,
        "outcome": "stored",
    }


def check_wifi_credential_material(
    helper_path: Path,
    credential_source_id: str,
    *,
    timeout_seconds: float = 30.0,
    runner: RunProcess = subprocess.run,
) -> HostWifiCredentialMaterialAvailabilityResult:
    """Check one opaque firmware provider item without loading its secret."""

    _validate_profile_id(credential_source_id)
    response = _run_macos_helper(
        helper_path,
        {
            "action": "check-credential-material",
            "profile_id": credential_source_id,
        },
        timeout_seconds=timeout_seconds,
        runner=runner,
    )
    available = response.get("profile_available")
    adapter = response.get("adapter")
    if not isinstance(available, bool) or not isinstance(adapter, str) or not adapter:
        raise HostWifiProfileError("host-wifi-response-invalid")
    return {
        "schema_version": 1,
        "adapter": adapter,
        "available": available,
    }


def store_wifi_profile_once(
    helper_path: Path,
    profile_id: str,
    ssid: str,
    password: str,
    *,
    timeout_seconds: float = 30.0,
    runner: RunProcess = subprocess.run,
) -> HostWifiProfileStoreResult:
    """Store one profile in the current user's secure OS credential store.

    This is an explicit local administration boundary. The secret is carried
    only in the helper's stdin and is never placed in argv, the environment,
    stdout, stderr, an evidence manifest, or source control.
    """

    _validate_profile_id(profile_id)
    if not 1 <= len(ssid.encode("utf-8")) <= 32:
        raise ValueError("SSID must contain between 1 and 32 UTF-8 bytes")
    if not 1 <= len(password.encode("utf-8")) <= 64:
        raise ValueError("Wi-Fi password must contain between 1 and 64 UTF-8 bytes")
    response = _run_macos_helper(
        helper_path,
        {
            "action": "store-profile",
            "profile_id": profile_id,
            "ssid": ssid,
            "password": password,
        },
        timeout_seconds=timeout_seconds,
        runner=runner,
    )
    adapter = response.get("adapter")
    if response.get("stored") is not True or not isinstance(adapter, str) or not adapter:
        raise HostWifiProfileError("host-wifi-response-invalid")
    return {
        "schema_version": 1,
        "adapter": adapter,
        "outcome": "stored",
    }
