"""File operations for configuration generation."""

from pathlib import Path
import sys
from typing import Any

import click
from pydantic import ValidationError
import yaml

from antipasta.core.config import AntipastaConfig


def _dump_scalar(value: Any) -> str:
    """Serialize a scalar to YAML without trailing newline."""
    dumped = yaml.safe_dump(
        value,
        allow_unicode=False,
        default_flow_style=False,
        sort_keys=False,
        explicit_end=False,
    ).strip()
    return dumped.splitlines()[0] if dumped else ""


def save_config(config: AntipastaConfig, output: Path) -> None:
    """Save configuration to file with helpful comments."""
    data = config.model_dump(exclude_none=True, mode="json")

    yaml_lines: list[str] = []
    yaml_lines.extend(
        [
            "# antipasta configuration file",
            "# Generated by: antipasta config generate",
            "",
        ]
    )

    _append_defaults_block(yaml_lines, data.get("defaults", {}))
    _append_languages_block(yaml_lines, data.get("languages") or [])
    _append_ignore_patterns_block(yaml_lines, data.get("ignore_patterns", []))

    yaml_lines.extend(
        [
            "",
            "# Whether to use .gitignore file for excluding files",
            f"use_gitignore: {_dump_scalar(data.get('use_gitignore', True))}",
        ]
    )

    # Write file
    try:
        output_path = Path(output)
        parent_dir = output_path.parent
        if parent_dir and not parent_dir.exists():
            try:
                parent_dir.mkdir(parents=True, exist_ok=True)
            except OSError as exc:
                raise PermissionError("Permission denied") from exc
        output_path.write_text("\n".join(yaml_lines) + "\n")

        click.echo(f"✅ Configuration saved to {output}")
        click.echo(f"\nRun 'antipasta config validate {output}' to verify.")
        click.echo("Run 'antipasta metrics' to start analyzing your code!")

    except Exception as e:
        click.echo(f"❌ Error saving configuration: {e}", err=True)
        sys.exit(1)


def _append_defaults_block(yaml_lines: list[str], defaults: dict[str, Any]) -> None:
    """Append the defaults section to the YAML output."""
    cyclo_max = _dump_scalar(defaults.get("max_cyclomatic_complexity", 10))
    cog_max = _dump_scalar(defaults.get("max_cognitive_complexity", 15))
    maint_min = _dump_scalar(defaults.get("min_maintainability_index", 50))
    halstead_volume = _dump_scalar(defaults.get("max_halstead_volume", 1000))
    halstead_difficulty = _dump_scalar(defaults.get("max_halstead_difficulty", 10))
    halstead_effort = _dump_scalar(defaults.get("max_halstead_effort", 10000))

    yaml_lines.extend(
        [
            "# Default thresholds for all languages",
            "defaults:",
            f"  max_cyclomatic_complexity: {cyclo_max}",
            f"  max_cognitive_complexity: {cog_max}",
            f"  min_maintainability_index: {maint_min}",
            "  # Halstead metrics (advanced)",
            f"  max_halstead_volume: {halstead_volume}",
            f"  max_halstead_difficulty: {halstead_difficulty}",
            f"  max_halstead_effort: {halstead_effort}",
            "",
        ]
    )


def _append_languages_block(yaml_lines: list[str], languages: list[dict[str, Any]]) -> None:
    """Append language configuration block."""
    yaml_lines.append("# Language-specific configurations")
    if not languages:
        yaml_lines.extend(("languages: []", ""))
        return

    yaml_lines.append("languages:")
    for lang in languages:
        _append_single_language(yaml_lines, lang)
    yaml_lines.append("")


def _append_single_language(yaml_lines: list[str], lang: dict[str, Any]) -> None:
    """Append a single language entry."""
    yaml_lines.append(f"  - name: {_dump_scalar(lang.get('name', ''))}")

    extensions = lang.get("extensions") or []
    if extensions:
        yaml_lines.append("    extensions:")
        for ext in extensions:
            yaml_lines.append(f"      - {_dump_scalar(ext)}")

    metrics = lang.get("metrics") or []
    if not metrics:
        yaml_lines.append("    metrics: []")
        return

    yaml_lines.append("    metrics:")
    for index, metric in enumerate(metrics):
        metric_type = _dump_scalar(metric.get("type"))
        threshold = _dump_scalar(metric.get("threshold"))
        comparison = _dump_scalar(metric.get("comparison"))
        yaml_lines.extend(
            (
                f"      - type: {metric_type}",
                f"        threshold: {threshold}",
                f"        comparison: {comparison}",
            )
        )
        if index < len(metrics) - 1:
            yaml_lines.append("")


def _append_ignore_patterns_block(yaml_lines: list[str], patterns: list[str]) -> None:
    """Append ignore pattern block."""
    yaml_lines.extend(
        [
            "",
            "# Files and patterns to ignore during analysis",
        ]
    )
    if not patterns:
        yaml_lines.append("ignore_patterns: []")
        return

    yaml_lines.append("ignore_patterns:")
    for pattern in patterns:
        yaml_lines.append(f"  - {_dump_scalar(pattern)}")


def confirm_file_overwrite(output: Path) -> bool:
    """Confirm file overwrite if file exists.

    Args:
        output: Output file path.

    Returns:
        True if should proceed, False otherwise.
    """
    click.echo(f"\nConfiguration will be saved to: {output}")

    if output.exists() and not click.confirm("File already exists. Overwrite?", default=False):
        click.echo("Aborted.")
        return False

    return True


def create_validated_config(config_dict: dict[str, Any]) -> AntipastaConfig:
    """Create and validate configuration object.

    Args:
        config_dict: Configuration dictionary.

    Returns:
        Validated AntipastaConfig object.

    Raises:
        SystemExit: If validation fails.
    """
    try:
        return AntipastaConfig(**config_dict)
    except ValidationError as e:
        handle_validation_error(e)
        sys.exit(1)
    except Exception as e:
        click.echo(f"\n❌ Unexpected error creating configuration: {e}", err=True)
        sys.exit(1)


def handle_validation_error(error: ValidationError) -> None:
    """Handle and display validation errors.

    Args:
        error: ValidationError from Pydantic.
    """
    click.echo("\n❌ Configuration validation failed:", err=True)
    for err in error.errors():
        loc = " -> ".join(str(x) for x in err["loc"])
        click.echo(f"  - {loc}: {err['msg']}", err=True)
    click.echo("\nPlease run the command again with valid values.", err=True)
