"""Configuration management for Honcho CLI.

Config stored at ``~/.honcho/config.json`` with env var overrides.

The CLI owns exactly two top-level keys in that file:

    apiKey          -- Honcho admin JWT
    environmentUrl  -- Honcho API URL (full URL, e.g. https://api.honcho.dev)

All other top-level keys (``hosts``, ``sessions``, ``saveMessages``,
``sessionStrategy``, …) are written by sibling Honcho tools and are
preserved untouched on save.

Workspace / peer / session scoping is intentionally *not* persisted here —
pass ``-w`` / ``-p`` / ``-s`` flags or set ``HONCHO_WORKSPACE_ID`` /
``HONCHO_PEER_ID`` / ``HONCHO_SESSION_ID`` per command instead.
"""

from __future__ import annotations

import json
import os
from dataclasses import dataclass, fields
from pathlib import Path

CONFIG_DIR = Path.home() / ".honcho"
CONFIG_FILE = CONFIG_DIR / "config.json"

DEFAULT_BASE_URL = "https://api.honcho.dev"

# Env var mapping for runtime overrides.
#
# Resolution order: flag > env var > config file > default.
ENV_MAP: dict[str, str] = {
    "api_key": "HONCHO_API_KEY",
    "base_url": "HONCHO_BASE_URL",
    "workspace_id": "HONCHO_WORKSPACE_ID",
    "peer_id": "HONCHO_PEER_ID",
    "session_id": "HONCHO_SESSION_ID",
}


@dataclass
class CLIConfig:
    """CLI configuration with layered resolution: flag > env > file > default.

    ``workspace_id`` / ``peer_id`` / ``session_id`` exist on this dataclass so
    flag/env overrides flow through ``get_client_kwargs()``, but they are
    never read from or written to the config file — they're per-command.
    """

    base_url: str = DEFAULT_BASE_URL
    api_key: str = ""
    workspace_id: str = ""
    peer_id: str = ""
    session_id: str = ""

    @classmethod
    def load(cls) -> CLIConfig:
        """Load config from file, then overlay env vars."""
        config = cls()

        if CONFIG_FILE.exists():
            try:
                with open(CONFIG_FILE, encoding="utf-8") as f:
                    data = json.load(f)
            except (json.JSONDecodeError, OSError):
                data = {}

            if isinstance(data, dict):
                url = data.get("environmentUrl")
                if isinstance(url, str) and url:
                    config.base_url = url
                key = data.get("apiKey")
                if isinstance(key, str):
                    config.api_key = key

        for fld_name, env_var in ENV_MAP.items():
            val = os.environ.get(env_var)
            if val:
                setattr(config, fld_name, val)
            elif val == "":
                # SDK reads these env vars directly and crashes on empty
                # strings with a Pydantic ValidationError. Drop them so the
                # SDK falls back to kwargs / defaults.
                os.environ.pop(env_var, None)

        return config

    def save(self) -> None:
        """Write ``apiKey`` + ``environmentUrl`` to config.json.

        Preserves unrelated top-level keys (``hosts``, ``sessions``,
        ``saveMessages``, ``sessionStrategy``, …) that other tools write.
        """
        CONFIG_DIR.mkdir(parents=True, exist_ok=True)

        data: dict = {}
        if CONFIG_FILE.exists():
            try:
                with open(CONFIG_FILE, encoding="utf-8") as f:
                    loaded = json.load(f)
                if isinstance(loaded, dict):
                    data = loaded
            except (json.JSONDecodeError, OSError):
                data = {}

        data["environmentUrl"] = self.base_url
        if self.api_key:
            data["apiKey"] = self.api_key
        else:
            data.pop("apiKey", None)

        CONFIG_FILE.write_text(json.dumps(data, indent=2) + "\n")
        # API key in plaintext — restrict to the owner on multi-user hosts.
        try:
            os.chmod(CONFIG_FILE, 0o600)
        except OSError:
            pass

    def redacted(self) -> dict[str, str]:
        """Return config dict with api_key redacted.

        Only includes fields that have a value set — per-command fields
        (workspace_id, peer_id, session_id) are omitted when empty.
        """
        d: dict[str, str] = {}
        for fld in fields(self):
            val = getattr(self, fld.name)
            if not val:
                continue
            if fld.name == "api_key":
                # Show ``***<last4>`` only — enough to compare keys without
                # leaking the header or body of the JWT.
                d[fld.name] = "***" + val[-4:] if len(val) > 4 else "***"
            else:
                d[fld.name] = val
        return d


def get_client_kwargs(config: CLIConfig) -> dict:
    """Build kwargs for Honcho client from config."""
    kwargs: dict = {}
    if config.base_url:
        kwargs["base_url"] = config.base_url
    if config.api_key:
        kwargs["api_key"] = config.api_key
    if config.workspace_id:
        kwargs["workspace_id"] = config.workspace_id
    return kwargs
