"""Unified auth state -- single file, single key namespace, atomic writes."""

from __future__ import annotations

import json
import logging
import os
import tempfile
from pathlib import Path
from typing import Any

logger = logging.getLogger(__name__)

AUTH_FILE = Path.home() / ".config" / "mcp-eregistrations-bpa" / "auth.json"
_VERSION = 1
_migrated = False


def _atomic_write(path: Path, data: dict[str, Any]) -> None:
    """Write JSON atomically via temp+rename (POSIX-safe)."""
    path.parent.mkdir(parents=True, exist_ok=True)
    fd, tmp = tempfile.mkstemp(dir=path.parent, suffix=".tmp")
    try:
        os.write(fd, json.dumps(data, indent=2).encode())
        os.close(fd)
        fd = -1  # Mark as closed
        os.chmod(tmp, 0o600)
        os.rename(tmp, str(path))
    except Exception:
        if fd >= 0:
            os.close(fd)
        try:
            os.unlink(tmp)
        except OSError:
            pass
        raise


def _load() -> dict[str, Any]:
    """Load auth.json or return empty structure."""
    if not AUTH_FILE.exists():
        return {"version": _VERSION, "instances": {}}
    try:
        data: dict[str, Any] = json.loads(AUTH_FILE.read_text())
        return data
    except Exception:
        return {"version": _VERSION, "instances": {}}


def _save(data: dict[str, Any]) -> None:
    _atomic_write(AUTH_FILE, data)


def _ensure_migrated() -> None:
    """Run one-time migration from old formats. Idempotent."""
    global _migrated
    if _migrated:
        return
    _migrated = True
    if AUTH_FILE.exists():
        return  # Already migrated
    _migrate()


def _get_instance(instance: str) -> dict[str, Any]:
    _ensure_migrated()
    data = _load()
    result: dict[str, Any] = data.get("instances", {}).get(instance, {})
    return result


def _set_instance(instance: str, fields: dict[str, Any]) -> None:
    _ensure_migrated()
    data = _load()
    data.setdefault("instances", {}).setdefault(instance, {}).update(fields)
    data["version"] = _VERSION
    _save(data)


# --- Public API ---


def get_credentials(instance: str) -> tuple[str, str] | None:
    inst = _get_instance(instance)
    u, p = inst.get("username"), inst.get("password")
    if u and p:
        return (u, p)
    return None


def store_credentials(
    instance: str,
    username: str,
    password: str,
    *,
    keycloak_url: str | None = None,
    keycloak_realm: str | None = None,
    client_id: str | None = None,
) -> None:
    fields: dict[str, Any] = {"username": username, "password": password}
    if keycloak_url:
        fields["keycloak_url"] = keycloak_url
    if keycloak_realm:
        fields["keycloak_realm"] = keycloak_realm
    if client_id:
        fields["client_id"] = client_id
    _set_instance(instance, fields)


def get_refresh_context(instance: str) -> dict[str, str] | None:
    inst = _get_instance(instance)
    rt = inst.get("refresh_token")
    if rt:
        ctx: dict[str, str] = {
            "refresh_token": rt,
            "token_endpoint": inst.get("token_endpoint", ""),
            "client_id": inst.get("client_id", ""),
        }
        # CAS-specific fields for refresh with Basic Auth
        cas_secret = inst.get("cas_client_secret")
        if cas_secret:
            ctx["cas_client_secret"] = cas_secret
        redirect_uri = inst.get("redirect_uri")
        if redirect_uri:
            ctx["redirect_uri"] = redirect_uri
        return ctx
    return None


def store_refresh_context(
    instance: str,
    *,
    refresh_token: str,
    token_endpoint: str,
    client_id: str,
    cas_client_secret: str | None = None,
    redirect_uri: str | None = None,
) -> None:
    fields: dict[str, str] = {
        "refresh_token": refresh_token,
        "token_endpoint": token_endpoint,
        "client_id": client_id,
    }
    if cas_client_secret:
        fields["cas_client_secret"] = cas_client_secret
    if redirect_uri:
        fields["redirect_uri"] = redirect_uri
    _set_instance(instance, fields)


def get_auth_config(instance: str) -> dict[str, str] | None:
    """Get keycloak_url + realm for password grant re-auth."""
    inst = _get_instance(instance)
    url = inst.get("keycloak_url")
    realm = inst.get("keycloak_realm")
    if url and realm:
        return {
            "keycloak_url": url,
            "keycloak_realm": realm,
            "client_id": inst.get("client_id", "mcp-eregistrations-bpa"),
        }
    return None


def get_cas_secret(instance: str) -> str | None:
    return _get_instance(instance).get("cas_client_secret")


def store_cas_secret(instance: str, secret: str) -> None:
    _set_instance(instance, {"cas_client_secret": secret})


def delete_instance(instance: str) -> None:
    _ensure_migrated()
    data = _load()
    data.get("instances", {}).pop(instance, None)
    _save(data)


# --- Migration (Step 2) ---


def _migrate() -> None:
    """One-time migration from old storage formats."""
    data: dict[str, Any] = {"version": _VERSION, "instances": {}}

    # Build slug -> profile name map
    slug_map = _build_slug_to_name_map()

    # 1. Migrate credentials.json
    cred_file = AUTH_FILE.parent / "credentials.json"
    if cred_file.exists():
        try:
            old = json.loads(cred_file.read_text())
            for slug, cred in old.get("credentials", {}).items():
                name = slug_map.get(slug, slug)  # Keep slug if unmappable
                data["instances"].setdefault(name, {}).update(cred)
            for slug, secret in old.get("cas_secrets", {}).items():
                name = slug_map.get(slug, slug)
                data["instances"].setdefault(name, {})["cas_client_secret"] = secret
            for slug, ctx in old.get("refresh_contexts", {}).items():
                name = slug_map.get(slug, slug)
                data["instances"].setdefault(name, {}).update(ctx)
        except Exception as e:
            logger.warning("credentials.json migration failed: %s", e)

    # 2. Migrate shared_tokens.json (already keyed by name)
    tokens_file = AUTH_FILE.parent / "shared_tokens.json"
    if tokens_file.exists():
        try:
            old = json.loads(tokens_file.read_text())
            for name, ctx in old.items():
                if name == "version":
                    continue
                data["instances"].setdefault(name, {}).update(ctx)
        except Exception as e:
            logger.warning("shared_tokens.json migration failed: %s", e)

    # 3. Migrate keyring (best-effort, one last read)
    try:
        import keyring

        for name in list(data["instances"].keys()):
            # Try reading refresh context from keyring
            raw = keyring.get_password("mcp-eregistrations", f"{name}:refresh")
            if raw:
                ctx = json.loads(raw)
                inst = data["instances"][name]
                if not inst.get("refresh_token"):
                    inst.update(ctx)
    except (ImportError, Exception):
        pass  # Keyring not available -- skip

    # 4. Enrich with keycloak_url + realm from profiles/instances.yaml
    try:
        from mcp_eregistrations_common.config import (
            load_builtin_instances,
        )

        # From profiles
        profiles_file = AUTH_FILE.parent / "profiles.json"
        if profiles_file.exists():
            profiles = json.loads(profiles_file.read_text()).get("profiles", {})
            for name, profile in profiles.items():
                inst = data["instances"].setdefault(name, {})
                if not inst.get("keycloak_url") and profile.get("keycloak_url"):
                    inst["keycloak_url"] = profile["keycloak_url"]
                    inst["keycloak_realm"] = profile.get("keycloak_realm", "")
                    inst["client_id"] = profile.get(
                        "keycloak_client_id", "mcp-eregistrations-bpa"
                    )
        # From built-in instances.yaml
        for builtin in load_builtin_instances():
            name = builtin["name"]
            inst = data["instances"].get(name)
            if inst and not inst.get("keycloak_url"):
                inst["keycloak_url"] = builtin.get("keycloak_url", "")
                inst["keycloak_realm"] = builtin.get("keycloak_realm", "")
                inst["client_id"] = builtin.get(
                    "keycloak_client_id", "mcp-eregistrations-bpa"
                )
    except Exception as e:
        logger.warning("Profile enrichment failed: %s", e)

    if data["instances"]:
        _save(data)


def _build_slug_to_name_map() -> dict[str, str]:
    """Build instance_id slug -> profile name mapping."""
    mapping: dict[str, str] = {}
    try:
        from mcp_eregistrations_common.config import (
            _generate_instance_slug,
            load_builtin_instances,
        )

        # Read legacy BPA profiles.json for migration
        profiles_file = AUTH_FILE.parent / "profiles.json"
        bpa_profiles: dict[str, Any] = {}
        if profiles_file.exists():
            try:
                bpa_profiles = json.loads(profiles_file.read_text()).get("profiles", {})
            except Exception:
                pass
        for name, profile in bpa_profiles.items():
            url = profile.get("bpa_instance_url", "")
            if url:
                mapping[_generate_instance_slug(url)] = name
        for inst in load_builtin_instances():
            mapping[_generate_instance_slug(inst["bpa_url"])] = inst["name"]
    except Exception:
        pass  # BPA not installed (standalone GDB) -- skip
    return mapping
