"""Helper script for config_resolution.robot end-to-end tests.

Each subcommand is a self-contained check that prints a sentinel on success.
"""

from __future__ import annotations

import os
import shutil
import sys
import tempfile
from collections.abc import Callable
from pathlib import Path
from unittest.mock import patch

# Ensure local source tree is importable
_SRC = str(Path(__file__).resolve().parents[1] / "src")
if _SRC not in sys.path:
    sys.path.insert(0, _SRC)

from cleveragents.application.services.config_service import (  # noqa: E402
    ConfigLevel,
    ConfigService,
)

# ---------------------------------------------------------------------------
# Helpers
# ---------------------------------------------------------------------------


def _make_service() -> tuple[ConfigService, Path]:
    """Create a ConfigService backed by an isolated temp directory."""
    tmpdir = Path(tempfile.mkdtemp())
    svc = ConfigService(
        config_dir=tmpdir,
        config_path=tmpdir / "config.toml",
        project_root=None,
    )
    return svc, tmpdir


def _cleanup(tmpdir: Path) -> None:
    """Remove the temp directory ignoring errors."""
    shutil.rmtree(str(tmpdir), ignore_errors=True)


# ---------------------------------------------------------------------------
# Subcommands
# ---------------------------------------------------------------------------


def registry_key_count() -> None:
    """Verify that the registry contains exactly 105 keys.

    102 spec-defined + 1 skills extension + 2 server stub keys
    (server.tls-verify, server.namespace).
    """
    registry = ConfigService.registry()
    count = len(registry)
    if count == 105:
        print("config-registry-key-count-ok")
    else:
        print(f"FAIL: expected 105 keys, got {count}", file=sys.stderr)
        sys.exit(1)


def resolve_default() -> None:
    """Verify default resolution with no overrides."""
    svc, tmpdir = _make_service()
    try:
        result = svc.resolve("core.log.level")
        if result.value == "FATAL" and result.source == ConfigLevel.DEFAULT:
            print("config-resolution-default-ok")
        else:
            print(
                f"FAIL: expected FATAL/default, got {result.value}/{result.source}",
                file=sys.stderr,
            )
            sys.exit(1)
    finally:
        _cleanup(tmpdir)


def resolve_global() -> None:
    """Verify that a TOML file value overrides the default."""
    svc, tmpdir = _make_service()
    try:
        svc.set_value("core.log.level", "DEBUG")
        result = svc.resolve("core.log.level")
        if result.value == "DEBUG" and result.source == ConfigLevel.GLOBAL:
            print("config-resolution-global-ok")
        else:
            print(
                f"FAIL: expected DEBUG/global, got {result.value}/{result.source}",
                file=sys.stderr,
            )
            sys.exit(1)
    finally:
        _cleanup(tmpdir)


def resolve_env() -> None:
    """Verify that env var overrides the global config value."""
    svc, tmpdir = _make_service()
    env_key = "CLEVERAGENTS_LOG_LEVEL"
    old_val = os.environ.get(env_key)
    try:
        # Set a global value first so we can prove env wins
        svc.set_value("core.log.level", "DEBUG")
        os.environ[env_key] = "WARNING"
        result = svc.resolve("core.log.level")
        if result.value == "WARNING" and result.source == ConfigLevel.ENV_VAR:
            print("config-resolution-env-ok")
        else:
            print(
                f"FAIL: expected WARNING/env_var, got {result.value}/{result.source}",
                file=sys.stderr,
            )
            sys.exit(1)
    finally:
        if old_val is None:
            os.environ.pop(env_key, None)
        else:
            os.environ[env_key] = old_val
        _cleanup(tmpdir)


def resolve_cli() -> None:
    """Verify that cli_value has the highest priority."""
    svc, tmpdir = _make_service()
    env_key = "CLEVERAGENTS_LOG_LEVEL"
    old_val = os.environ.get(env_key)
    try:
        # Set all lower levels so we can prove CLI wins
        svc.set_value("core.log.level", "DEBUG")
        os.environ[env_key] = "WARNING"
        result = svc.resolve("core.log.level", cli_value="ERROR")
        if result.value == "ERROR" and result.source == ConfigLevel.CLI_FLAG:
            print("config-resolution-cli-ok")
        else:
            print(
                f"FAIL: expected ERROR/cli_flag, got {result.value}/{result.source}",
                file=sys.stderr,
            )
            sys.exit(1)
    finally:
        if old_val is None:
            os.environ.pop(env_key, None)
        else:
            os.environ[env_key] = old_val
        _cleanup(tmpdir)


def resolve_project() -> None:
    """Verify project-scoped config resolution via project_name."""
    svc, tmpdir = _make_service()
    try:
        # Write project-scoped override into the TOML file
        import tomlkit

        doc = tomlkit.document()
        project_table = tomlkit.table()
        myproj_table = tomlkit.table()
        myproj_table["core.automation-profile"] = "full-auto"
        project_table["myproject"] = myproj_table
        doc["project"] = project_table
        svc._config_dir.mkdir(parents=True, exist_ok=True)
        with open(svc._config_path, "w") as fh:
            tomlkit.dump(doc, fh)

        result = svc.resolve("core.automation-profile", project_name="myproject")
        if result.value == "full-auto" and result.source == ConfigLevel.PROJECT:
            print("config-resolution-project-ok")
        else:
            print(
                f"FAIL: expected full-auto/project, got {result.value}/{result.source}",
                file=sys.stderr,
            )
            sys.exit(1)
    finally:
        _cleanup(tmpdir)


def resolve_verbose_chain() -> None:
    """Verify verbose=True populates all 6 chain entries."""
    svc, tmpdir = _make_service()
    try:
        result = svc.resolve("core.log.level", verbose=True)
        chain_len = len(result.chain)
        if chain_len == 6:
            # Verify the sources in order
            expected_sources = [
                ConfigLevel.CLI_FLAG.value,
                ConfigLevel.ENV_VAR.value,
                ConfigLevel.LOCAL.value,
                ConfigLevel.PROJECT.value,
                ConfigLevel.GLOBAL.value,
                ConfigLevel.DEFAULT.value,
            ]
            actual_sources = [entry["source"] for entry in result.chain]
            if actual_sources == expected_sources:
                print("config-resolution-verbose-chain-ok")
            else:
                print(
                    f"FAIL: chain sources mismatch: {actual_sources}",
                    file=sys.stderr,
                )
                sys.exit(1)
        else:
            print(
                f"FAIL: expected 6 chain entries, got {chain_len}",
                file=sys.stderr,
            )
            sys.exit(1)
    finally:
        _cleanup(tmpdir)


def env_var_provider() -> None:
    """Verify that provider.openai.api-key maps to OPENAI_API_KEY."""
    env_name = ConfigService.env_var_for_key("provider.openai.api-key")
    if env_name == "OPENAI_API_KEY":
        print("config-env-var-provider-ok")
    else:
        print(
            f"FAIL: expected OPENAI_API_KEY, got {env_name}",
            file=sys.stderr,
        )
        sys.exit(1)


def type_coercion() -> None:
    """Verify str→int, str→float, and str→bool coercion."""
    errors: list[str] = []

    # str → int
    result_int = ConfigService.validate_type("core.log.retention-days", "45")
    if result_int != 45 or not isinstance(result_int, int):
        errors.append(
            f"int coercion: expected 45 (int), got {result_int!r} "
            f"({type(result_int).__name__})"
        )

    # str → float
    result_float = ConfigService.validate_type("plan.budget.per-plan", "3.14")
    if result_float != 3.14 or not isinstance(result_float, float):
        errors.append(
            f"float coercion: expected 3.14 (float), got {result_float!r} "
            f"({type(result_float).__name__})"
        )

    # str → bool (true)
    result_bool_t = ConfigService.validate_type("core.log.file-enabled", "true")
    if result_bool_t is not True:
        errors.append(f"bool coercion (true): got {result_bool_t!r}")

    # str → bool (false)
    result_bool_f = ConfigService.validate_type("core.log.file-enabled", "false")
    if result_bool_f is not False:
        errors.append(f"bool coercion (false): got {result_bool_f!r}")

    if errors:
        for err in errors:
            print(f"FAIL: {err}", file=sys.stderr)
        sys.exit(1)
    else:
        print("config-type-coercion-ok")


def cli_list_integration() -> None:
    """Verify ``agents config list`` outputs all registered keys."""
    from typer.testing import CliRunner

    from cleveragents.cli.commands import config as config_mod
    from cleveragents.cli.commands.config import app as config_app

    runner = CliRunner()
    tmpdir = Path(tempfile.mkdtemp())
    tmppath = tmpdir / "config.toml"

    try:
        with (
            patch.object(config_mod, "_CONFIG_DIR", tmpdir),
            patch.object(config_mod, "_CONFIG_PATH", tmppath),
        ):
            result = runner.invoke(config_app, ["list"])

        if result.exit_code != 0:
            print(f"FAIL: config list exited {result.exit_code}", file=sys.stderr)
            print(result.output, file=sys.stderr)
            sys.exit(1)

        output = result.output

        # Spot-check several representative keys from different sections
        missing: list[str] = []
        for key_fragment in ["log.level", "automation-profile", "openai.api-key"]:
            if key_fragment not in output:
                missing.append(key_fragment)

        if missing:
            print(
                f"FAIL: missing key fragments in output: {missing}",
                file=sys.stderr,
            )
            sys.exit(1)

        print("config-cli-list-integration-ok")
    finally:
        _cleanup(tmpdir)


# ---------------------------------------------------------------------------
# Dispatcher
# ---------------------------------------------------------------------------

_COMMANDS: dict[str, Callable[[], None]] = {
    "registry-key-count": registry_key_count,
    "resolve-default": resolve_default,
    "resolve-global": resolve_global,
    "resolve-env": resolve_env,
    "resolve-cli": resolve_cli,
    "resolve-project": resolve_project,
    "resolve-verbose-chain": resolve_verbose_chain,
    "env-var-provider": env_var_provider,
    "type-coercion": type_coercion,
    "cli-list-integration": cli_list_integration,
}

if __name__ == "__main__":
    if len(sys.argv) < 2 or sys.argv[1] not in _COMMANDS:
        print(f"Usage: {sys.argv[0]} <{'|'.join(_COMMANDS)}>", file=sys.stderr)
        sys.exit(2)
    _COMMANDS[sys.argv[1]]()
