"""Bandito configuration — read/write ~/.bandito/config.toml, interactive setup."""

from __future__ import annotations

import os
import sys
from dataclasses import dataclass, field
from pathlib import Path


_CONFIG_PATH = Path.home() / ".bandito" / "config.toml"

_DEFAULT_TRACES_DIR = str(Path.home() / ".bandito" / "traces")


@dataclass
class S3Settings:
    bucket: str | None = None
    endpoint: str | None = None
    access_key: str | None = None
    secret_key: str | None = None
    prefix: str = ""
    region: str | None = None


@dataclass
class PostgresSettings:
    url: str | None = None


@dataclass
class LangfuseSettings:
    public_key: str | None = None
    secret_key: str | None = None
    base_url: str | None = None


@dataclass
class StorageSettings:
    backend: str = "local"
    directory: str | None = None  # for local backend
    s3: S3Settings = field(default_factory=S3Settings)
    postgres: PostgresSettings = field(default_factory=PostgresSettings)
    langfuse: LangfuseSettings = field(default_factory=LangfuseSettings)


@dataclass
class ProjectSettings:
    traces_dir: str | None = None


@dataclass
class CacheSettings:
    enabled: bool = True


@dataclass
class SyncSettings:
    enabled: bool = True  # Sync layer 2 to API


@dataclass
class AgentSettings:
    enabled: bool = True
    mcp_enabled: bool = False  # Enable MCP server for agent tool access


@dataclass
class BanditoConfig:
    traces_dir: str = _DEFAULT_TRACES_DIR
    api_key: str | None = None
    cache: CacheSettings = field(default_factory=CacheSettings)
    sync: SyncSettings = field(default_factory=SyncSettings)
    agent: AgentSettings = field(default_factory=AgentSettings)
    storage: StorageSettings = field(default_factory=StorageSettings)
    projects: dict[str, ProjectSettings] = field(default_factory=dict)


def load_config(path: str | Path | None = None) -> BanditoConfig:
    path = Path(path) if path else _CONFIG_PATH
    if not path.exists():
        return BanditoConfig()
    raw = _read_toml(str(path))
    return _dict_to_config(raw)


def save_config(config: BanditoConfig, path: str | Path | None = None) -> Path:
    path = Path(path) if path else _CONFIG_PATH
    path.parent.mkdir(parents=True, exist_ok=True)
    path.write_text(_config_to_toml(config))
    os.chmod(path, 0o600)
    return path


def resolve_traces_dir(config: BanditoConfig, project: str | None = None) -> str:
    env = os.environ.get("BANDITO_TRACES_DIR")
    if env:
        return env
    if project and project in config.projects:
        proj_dir = config.projects[project].traces_dir
        if proj_dir:
            return proj_dir
    if config.storage.directory:
        return config.storage.directory
    return config.traces_dir


def resolve_cache_enabled(config: BanditoConfig) -> bool:
    if not config.cache.enabled:
        return False
    if config.storage.backend != "langfuse":
        return False
    return True


def resolve_sync_enabled(config: BanditoConfig) -> bool:
    """Check if layer 2 sync to API is enabled."""
    if not config.sync.enabled:
        return False
    if not config.api_key and not os.environ.get("BANDITO_API_KEY"):
        return False
    return True


# -- Interactive config -------------------------------------------------------


def run_config_interactive(path: str | Path | None = None) -> None:
    path = Path(path) if path else _CONFIG_PATH
    config = load_config(path)

    print(f"\n  bandito config \u00b7 {path}\n")

    # Storage backend
    print("  Storage backend for traces.")
    print("  'local' stores JSON files on disk (default).")
    print()
    config.storage.backend = _prompt("Storage backend (local)", config.storage.backend)

    # Traces directory
    print()
    print("  Where Bandito stores and reads trace data.")
    print("  Your app writes here via bandito.trace(), and the")
    print("  CLI reads from here for analysis and grading.")
    print()
    config.traces_dir = _prompt("Traces directory", config.traces_dir)

    # Projects
    if config.projects:
        print("\n  Projects:")
        for name, proj in sorted(config.projects.items()):
            dir_info = proj.traces_dir or config.traces_dir
            print(f"    {name} ({dir_info})")

    # Projects — only ask for traces directory override
    print()
    print("  Projects let you point different apps at different trace")
    print("  directories. Skip this if all your projects share one location.")

    while True:
        label = "Add/edit a project" if config.projects else "Add a project"
        name = _prompt(f"\n  {label}? (name or enter to skip)", "")
        if not name:
            break
        proj = config.projects.get(name, ProjectSettings())
        proj.traces_dir = _prompt(
            f"    Traces directory for {name}", proj.traces_dir or config.traces_dir
        )
        # If same as global, don't store it
        if proj.traces_dir == config.traces_dir:
            proj.traces_dir = None
        # Only store project if it has overrides
        if proj.traces_dir:
            config.projects[name] = proj
        elif name in config.projects:
            del config.projects[name]

    saved = save_config(config, path)
    print(f"\n  Saved {saved}\n")


def _prompt(label: str, default: str) -> str:
    try:
        value = input(f"  {label} [{default}]: ").strip()
    except (EOFError, KeyboardInterrupt):
        print()
        sys.exit(0)
    return value if value else default


# -- TOML read/write ---------------------------------------------------------


def _read_toml(path: str) -> dict:
    try:
        import tomllib  # type: ignore[import-not-found]
    except ImportError:
        try:
            import tomli as tomllib  # type: ignore[import-not-found,no-redef]
        except ImportError:
            try:
                import toml as toml_lib  # type: ignore[import-not-found,no-redef]
            except ImportError:
                import rtoml as toml_lib  # type: ignore[import-not-found,no-redef]

            with open(path) as f:
                return toml_lib.load(f)

    with open(path, "rb") as f:
        return tomllib.load(f)


def _dict_to_config(raw: dict) -> BanditoConfig:
    config = BanditoConfig()
    config.traces_dir = raw.get("traces_dir", _DEFAULT_TRACES_DIR)
    config.api_key = raw.get("api_key")

    cache = raw.get("cache", {})
    config.cache.enabled = cache.get("enabled", True)

    sync = raw.get("sync", {})
    config.sync.enabled = sync.get("enabled", True)

    agent = raw.get("agent", {})
    config.agent.enabled = agent.get("enabled", True)
    config.agent.mcp_enabled = agent.get("mcp_enabled", False)

    storage = raw.get("storage", {})
    config.storage = _parse_storage(storage)

    for name, proj_raw in raw.get("projects", {}).items():
        proj = ProjectSettings()
        proj.traces_dir = proj_raw.get("traces_dir")
        config.projects[name] = proj

    return config


def _parse_storage(raw: dict) -> StorageSettings:
    settings = StorageSettings()
    settings.backend = raw.get("backend", "local")
    settings.directory = raw.get("directory")

    s3 = raw.get("s3", {})
    settings.s3.bucket = s3.get("bucket")
    settings.s3.endpoint = s3.get("endpoint")
    settings.s3.access_key = s3.get("access_key")
    settings.s3.secret_key = s3.get("secret_key")
    settings.s3.prefix = s3.get("prefix", "")
    settings.s3.region = s3.get("region")

    pg = raw.get("postgres", {})
    settings.postgres.url = pg.get("url")

    lf = raw.get("langfuse", {})
    settings.langfuse.public_key = lf.get("public_key")
    settings.langfuse.secret_key = lf.get("secret_key")
    settings.langfuse.base_url = lf.get("base_url")

    return settings


def _config_to_toml(config: BanditoConfig) -> str:
    lines = [f'traces_dir = "{config.traces_dir}"']
    if config.api_key:
        lines.append(f'api_key = "{config.api_key}"')

    # Cache section
    if not config.cache.enabled:
        lines.append("")
        lines.append("[cache]")
        lines.append('enabled = false')

    # Sync section
    if not config.sync.enabled:
        lines.append("")
        lines.append("[sync]")
        lines.append('enabled = false')

    # Agent section
    if not config.agent.enabled:
        lines.append("")
        lines.append("[agent]")
        lines.append('enabled = false')
    if config.agent.mcp_enabled:
        lines.append("")
        lines.append("[agent]")
        lines.append('enabled = true')
        lines.append('mcp_enabled = true')

    # Storage section
    if config.storage.backend != "local" or config.storage.directory:
        _write_storage_toml(lines, "storage", config.storage)

    for name in sorted(config.projects):
        proj = config.projects[name]
        has_content = proj.traces_dir
        if not has_content:
            continue
        lines.append("")
        lines.append(f"[projects.{name}]")
        if proj.traces_dir:
            lines.append(f'traces_dir = "{proj.traces_dir}"')

    lines.append("")
    return "\n".join(lines)


def _write_storage_toml(lines: list[str], section: str, s: StorageSettings) -> None:
    lines.append("")
    lines.append(f"[{section}]")
    lines.append(f'backend = "{s.backend}"')
    if s.directory:
        lines.append(f'directory = "{s.directory}"')
    if s.backend == "s3" and s.s3.bucket:
        lines.append("")
        lines.append(f"[{section}.s3]")
        lines.append(f'bucket = "{s.s3.bucket}"')
        if s.s3.endpoint:
            lines.append(f'endpoint = "{s.s3.endpoint}"')
        if s.s3.access_key:
            lines.append(f'access_key = "{s.s3.access_key}"')
        if s.s3.secret_key:
            lines.append(f'secret_key = "{s.s3.secret_key}"')
        if s.s3.prefix:
            lines.append(f'prefix = "{s.s3.prefix}"')
        if s.s3.region:
            lines.append(f'region = "{s.s3.region}"')
    if s.backend == "postgres" and s.postgres.url:
        lines.append("")
        lines.append(f"[{section}.postgres]")
        lines.append(f'url = "{s.postgres.url}"')
    if s.backend == "langfuse" and s.langfuse.public_key:
        lines.append("")
        lines.append(f"[{section}.langfuse]")
        lines.append(f'public_key = "{s.langfuse.public_key}"')
        if s.langfuse.secret_key:
            lines.append(f'secret_key = "{s.langfuse.secret_key}"')
        if s.langfuse.base_url:
            lines.append(f'base_url = "{s.langfuse.base_url}"')
