"""Configuration helpers for the iap-agent CLI."""

from __future__ import annotations

import os
from dataclasses import dataclass
from pathlib import Path
from typing import Any

DEFAULT_CONFIG_PATH = Path.home() / ".iap_agent" / "config.toml"
DEFAULT_REGISTRY_BASE = "https://registry.ia-protocol.com"
REGISTRY_BASE_ENV_VAR = "IAP_REGISTRY_BASE"
REGISTRY_API_KEY_ENV_VAR = "IAP_REGISTRY_API_KEY"
ACCOUNT_TOKEN_ENV_VAR = "IAP_ACCOUNT_TOKEN"


@dataclass(frozen=True)
class CLIConfig:
    config_schema_version: int = 1
    beta_mode: bool = True
    maturity_level: str = "beta"
    registry_base: str = DEFAULT_REGISTRY_BASE
    registry_api_key: str | None = None
    account_token: str | None = None
    agent_name: str = "Local Agent"
    amcs_db_path: str = "./amcs.db"
    sessions_dir: str = str(Path.home() / ".iap_agent" / "sessions")
    registry_public_key_b64: str | None = None


class ConfigError(ValueError):
    """Raised when CLI config is invalid."""


_CLI_CONFIG_FIELDS = {
    "config_schema_version",
    "beta_mode",
    "maturity_level",
    "registry_base",
    "registry_api_key",
    "account_token",
    "agent_name",
    "amcs_db_path",
    "sessions_dir",
    "registry_public_key_b64",
}


def _load_toml(path: Path) -> dict[str, Any]:
    raw = path.read_text(encoding="utf-8")

    try:  # Python 3.11+
        import tomllib  # type: ignore[attr-defined]
        try:
            return tomllib.loads(raw)
        except tomllib.TOMLDecodeError as exc:
            raise ConfigError(f"invalid TOML in {path}: {exc}") from exc
    except ModuleNotFoundError:
        try:
            import tomli
        except ModuleNotFoundError as exc:
            raise ConfigError("toml parser unavailable; install tomli for Python < 3.11") from exc
        try:
            return tomli.loads(raw)
        except tomli.TOMLDecodeError as exc:
            raise ConfigError(f"invalid TOML in {path}: {exc}") from exc


def _to_bool(value: Any, field_name: str) -> bool:
    if isinstance(value, bool):
        return value
    if isinstance(value, str):
        lowered = value.strip().lower()
        if lowered in {"true", "1", "yes", "on"}:
            return True
        if lowered in {"false", "0", "no", "off"}:
            return False
    raise ConfigError(f"{field_name} must be a boolean")


def _to_positive_int(value: Any, field_name: str, *, default: int) -> int:
    if value is None:
        return default
    try:
        parsed = int(value)
    except (TypeError, ValueError) as exc:
        raise ConfigError(f"{field_name} must be an integer") from exc
    if parsed < 1:
        raise ConfigError(f"{field_name} must be >= 1")
    return parsed


def load_cli_config(path: str | Path | None = None) -> CLIConfig:
    config_path = Path(path) if path else DEFAULT_CONFIG_PATH
    if not config_path.exists():
        return CLIConfig()

    parsed = _load_toml(config_path)
    section = parsed.get("cli")
    if isinstance(section, dict):
        source = section
    elif section is None:
        source = parsed
    else:
        raise ConfigError("[cli] must be a table")

    config_schema_version = _to_positive_int(
        source.get("config_schema_version"),
        "config_schema_version",
        default=1,
    )
    beta_mode = _to_bool(source.get("beta_mode", True), "beta_mode")
    maturity_level = str(source.get("maturity_level", "beta")).strip().lower()
    if maturity_level not in {"alpha", "beta", "stable"}:
        raise ConfigError("maturity_level must be one of: alpha, beta, stable")

    env_registry_base = os.getenv(REGISTRY_BASE_ENV_VAR)
    env_registry_api_key = os.getenv(REGISTRY_API_KEY_ENV_VAR)
    env_account_token = os.getenv(ACCOUNT_TOKEN_ENV_VAR)
    configured_registry_base = str(source.get("registry_base", DEFAULT_REGISTRY_BASE)).strip()
    registry_base = env_registry_base.strip() if env_registry_base else configured_registry_base
    if not registry_base:
        raise ConfigError("registry_base must not be empty")

    configured_registry_api_key_raw = source.get("registry_api_key")
    if env_registry_api_key is not None:
        registry_api_key = env_registry_api_key.strip() or None
    elif configured_registry_api_key_raw is None:
        registry_api_key = None
    else:
        registry_api_key = str(configured_registry_api_key_raw).strip() or None

    configured_account_token_raw = source.get("account_token")
    if env_account_token is not None:
        account_token = env_account_token.strip() or None
    elif configured_account_token_raw is None:
        account_token = None
    else:
        account_token = str(configured_account_token_raw).strip() or None

    agent_name = str(source.get("agent_name", "Local Agent")).strip()
    if not agent_name:
        raise ConfigError("agent_name must not be empty")

    amcs_db_path = str(source.get("amcs_db_path", "./amcs.db")).strip()
    if not amcs_db_path:
        raise ConfigError("amcs_db_path must not be empty")

    default_sessions_dir = str(Path.home() / ".iap_agent" / "sessions")
    sessions_dir = str(source.get("sessions_dir", default_sessions_dir)).strip()
    if not sessions_dir:
        raise ConfigError("sessions_dir must not be empty")
    registry_public_key_b64_raw = source.get("registry_public_key_b64")
    if registry_public_key_b64_raw is None:
        registry_public_key_b64 = None
    else:
        registry_public_key_b64 = str(registry_public_key_b64_raw).strip() or None

    return CLIConfig(
        config_schema_version=config_schema_version,
        beta_mode=beta_mode,
        maturity_level=maturity_level,
        registry_base=registry_base,
        registry_api_key=registry_api_key,
        account_token=account_token,
        agent_name=agent_name,
        amcs_db_path=amcs_db_path,
        sessions_dir=sessions_dir,
        registry_public_key_b64=registry_public_key_b64,
    )


def _format_toml_scalar(value: Any) -> str:
    if isinstance(value, bool):
        return "true" if value else "false"
    if isinstance(value, int):
        return str(value)
    escaped = str(value).replace("\\", "\\\\").replace('"', '\\"')
    return f'"{escaped}"'


def save_cli_setting(path: str | Path | None, key: str, value: Any | None) -> Path:
    if key not in _CLI_CONFIG_FIELDS:
        raise ConfigError(f"unsupported config key: {key}")

    config_path = Path(path) if path else DEFAULT_CONFIG_PATH
    parsed: dict[str, Any] = {}
    if config_path.exists():
        parsed = _load_toml(config_path)

    if "cli" in parsed:
        cli_section_raw = parsed.get("cli")
        if cli_section_raw is None:
            cli_section: dict[str, Any] = {}
        elif isinstance(cli_section_raw, dict):
            cli_section = dict(cli_section_raw)
        else:
            raise ConfigError("[cli] must be a table")
    else:
        cli_section = {}
        for field in _CLI_CONFIG_FIELDS:
            if field in parsed:
                cli_section[field] = parsed[field]

    if value is None:
        cli_section.pop(key, None)
    else:
        cli_section[key] = value

    lines = ["[cli]"]
    for field in sorted(cli_section):
        lines.append(f"{field} = {_format_toml_scalar(cli_section[field])}")

    config_path.parent.mkdir(parents=True, exist_ok=True)
    config_path.write_text("\n".join(lines) + "\n", encoding="utf-8")
    return config_path
