"""Configuration command group for the Dorgy CLI."""

from __future__ import annotations

import difflib

import click
import yaml
from rich.syntax import Syntax

from dorgy.cli.context import console
from dorgy.cli.helpers.configuration import _assign_nested
from dorgy.config import (
    ConfigError,
    DorgyConfig,
    ensure_config,
    load_config,
    load_file_overrides,
    read_config_text,
    resolve_with_precedence,
    save_config,
)


@click.group()
def config() -> None:
    """Manage Dorgy configuration files and overrides."""


@config.command("view")
@click.option("--no-env", is_flag=True, help="Ignore environment overrides when displaying output.")
def config_view(no_env: bool) -> None:
    """Display the effective configuration after applying precedence rules.

    Args:
        no_env: When True, exclude environment overrides from the rendered config.

    Raises:
        click.ClickException: If the configuration cannot be loaded.
    """
    try:
        ensure_config()
        config = load_config(include_env=not no_env)
    except ConfigError as exc:
        raise click.ClickException(str(exc)) from exc

    yaml_text = yaml.safe_dump(config.model_dump(mode="python"), sort_keys=False)
    console.print(Syntax(yaml_text, "yaml", word_wrap=True))


@config.command("set")
@click.argument("key")
@click.option("--value", required=True, help="Value to assign to KEY.")
def config_set(key: str, value: str) -> None:
    """Persist a configuration value expressed as a dotted ``KEY``.

    Args:
        key: Dotted configuration path to update.
        value: Raw YAML string representing the new value.

    Raises:
        click.ClickException: If the update fails validation or parsing.
    """
    ensure_config()

    before = read_config_text().splitlines()
    segments = [segment.strip() for segment in key.split(".") if segment.strip()]
    if not segments:
        raise click.ClickException("KEY must specify a dotted path such as 'llm.temperature'.")

    try:
        parsed_value = yaml.safe_load(value)
    except yaml.YAMLError as exc:
        raise click.ClickException(f"Unable to parse value: {exc}") from exc

    file_data = load_file_overrides()

    try:
        _assign_nested(file_data, segments, parsed_value)
    except ConfigError as exc:
        raise click.ClickException(str(exc)) from exc

    try:
        resolve_with_precedence(defaults=DorgyConfig(), file_overrides=file_data)
    except ConfigError as exc:
        raise click.ClickException(str(exc)) from exc

    save_config(file_data)
    after = read_config_text().splitlines()

    diff = list(
        difflib.unified_diff(
            before,
            after,
            fromfile="config.yaml (before)",
            tofile="config.yaml (after)",
            lineterm="",
        )
    )

    if diff:
        console.print(Syntax("\n".join(diff), "diff", word_wrap=False))
    else:
        console.print("[yellow]No changes applied; value already up to date.[/yellow]")
        return

    console.print(f"[green]Updated {'.'.join(segments)}.[/green]")


@config.command("edit")
def config_edit() -> None:
    """Open the configuration file in an interactive editor session.

    Raises:
        click.ClickException: If the edited content fails validation.
    """
    ensure_config()

    original = read_config_text()
    edited = click.edit(original, extension=".yaml")

    if edited is None:
        console.print("[yellow]Edit cancelled; no changes applied.[/yellow]")
        return

    if edited == original:
        console.print("[yellow]No changes detected.[/yellow]")
        return

    try:
        parsed = yaml.safe_load(edited) or {}
    except yaml.YAMLError as exc:
        raise click.ClickException(f"Invalid YAML: {exc}") from exc

    if not isinstance(parsed, dict):
        raise click.ClickException("Configuration file must contain a top-level mapping.")

    try:
        resolve_with_precedence(defaults=DorgyConfig(), file_overrides=parsed)
    except ConfigError as exc:
        raise click.ClickException(str(exc)) from exc

    save_config(parsed)
    console.print("[green]Configuration updated successfully.[/green]")


def register_config_group(cli: click.Group) -> None:
    """Register the configuration group with the top-level CLI.

    Args:
        cli: Root Click command group.
    """

    cli.add_command(config)


__all__ = ["config", "register_config_group"]
