"""TOML configuration generator and file handling."""

from __future__ import annotations

import os
import shutil
import tomllib
from datetime import UTC, datetime
from pathlib import Path
from typing import Any

# Default config paths - use XDG_CONFIG_HOME with fallback to ~/.config
_xdg_config_home = Path(os.environ.get("XDG_CONFIG_HOME") or Path.home() / ".config")
DEFAULT_CONFIG_PATH = _xdg_config_home / "pypr" / "config.toml"
LEGACY_CONFIG_PATH = _xdg_config_home / "hypr" / "pyprland.toml"

# Max items for inline dict rendering
MAX_INLINE_DICT_ITEMS = 3


def get_config_path() -> Path:
    """Get the config path, checking for legacy location.

    Returns:
        Path to use for configuration
    """
    if LEGACY_CONFIG_PATH.exists() and not DEFAULT_CONFIG_PATH.exists():
        return LEGACY_CONFIG_PATH
    return DEFAULT_CONFIG_PATH


def backup_config(config_path: Path) -> Path | None:
    """Create a timestamped backup of existing config.

    Args:
        config_path: Path to the config file

    Returns:
        Path to backup file, or None if no backup needed
    """
    if not config_path.exists():
        return None

    timestamp = datetime.now(tz=UTC).strftime("%Y%m%d_%H%M%S")
    backup_path = config_path.with_suffix(f".{timestamp}.bak")

    shutil.copy2(config_path, backup_path)
    return backup_path


def format_toml_value(value: Any, indent: int = 0) -> str:  # noqa: PLR0911
    # pylint: disable=too-many-return-statements
    """Format a Python value as TOML.

    Args:
        value: Value to format
        indent: Current indentation level

    Returns:
        TOML string representation
    """
    if value is None:
        return '""'

    if isinstance(value, bool):
        return "true" if value else "false"

    if isinstance(value, int | float):
        return str(value)

    if isinstance(value, str):
        # Escape special characters and wrap in quotes
        escaped = value.replace("\\", "\\\\").replace('"', '\\"')
        if "\n" in escaped:
            # Use TOML multiline basic string for values containing newlines
            return f'"""\n{escaped}"""'
        return f'"{escaped}"'

    if isinstance(value, list):
        if not value:
            return "[]"
        # Simple inline list for short items
        if all(isinstance(x, str | int | float | bool) for x in value):
            items = ", ".join(format_toml_value(x) for x in value)
            return f"[{items}]"
        # Multi-line list for complex items
        items = ",\n".join("  " * (indent + 1) + format_toml_value(x, indent + 1) for x in value)
        return f"[\n{items}\n" + "  " * indent + "]"

    if isinstance(value, dict):
        # Inline table for simple dicts
        items = ", ".join(f"{k} = {format_toml_value(v)}" for k, v in value.items())
        return f"{{ {items} }}"

    return f'"{value!s}"'


def generate_toml(config: dict) -> str:  # noqa: C901
    """Generate TOML string from configuration dict.

    The config structure expected:
    {
        "plugins": ["plugin1", "plugin2"],
        "plugin1": {...},
        "plugin2": {...},
        "plugin2.subsection": {...},  # For things like scratchpads.term
    }

    Args:
        config: Configuration dictionary

    Returns:
        TOML formatted string
    """
    lines = []

    # Header comment
    lines.append("# Pyprland configuration")
    lines.append("# Generated by pypr-quickstart")
    lines.append(f"# {datetime.now(tz=UTC).strftime('%Y-%m-%d %H:%M:%S')} UTC")
    lines.append("")

    # Process pyprland core config first
    if "pyprland" in config:
        lines.append("[pyprland]")
        for key, value in config["pyprland"].items():
            lines.append(f"{key} = {format_toml_value(value)}")
        lines.append("")

    # Process each plugin section
    for section_name, section_data in config.items():
        if section_name == "pyprland":
            continue

        if not isinstance(section_data, dict):
            continue

        lines.append(f"[{section_name}]")

        # Separate simple values from subsections
        simple_values = {}
        subsections = {}

        for key, value in section_data.items():
            if isinstance(value, dict) and not _is_inline_dict(value):
                subsections[key] = value
            else:
                simple_values[key] = value

        # Write simple values
        for key, value in simple_values.items():
            lines.append(f"{key} = {format_toml_value(value)}")

        lines.append("")

        # Write subsections
        for sub_name, sub_data in subsections.items():
            lines.append(f"[{section_name}.{sub_name}]")
            for key, value in sub_data.items():
                lines.append(f"{key} = {format_toml_value(value)}")
            lines.append("")

    return "\n".join(lines)


def _is_inline_dict(data: dict) -> bool:
    """Check if a dict should be rendered inline.

    Small dicts with simple values are rendered inline.

    Args:
        data: Dictionary to check

    Returns:
        True if should be inline
    """
    if len(data) > MAX_INLINE_DICT_ITEMS:
        return False
    return all(isinstance(v, str | int | float | bool) for v in data.values())


def write_config(
    config: dict,
    output_path: Path | None = None,
    dry_run: bool = False,
) -> tuple[Path, str]:
    """Write configuration to file.

    Args:
        config: Configuration dictionary
        output_path: Custom output path (uses default if None)
        dry_run: If True, don't write file

    Returns:
        Tuple of (path, toml_content)
    """
    path = output_path or get_config_path()
    content = generate_toml(config)

    if not dry_run:
        # Ensure directory exists
        path.parent.mkdir(parents=True, exist_ok=True)
        path.write_text(content, encoding="utf-8")

    return path, content


def merge_config(existing: dict, new: dict) -> dict:
    """Merge new config into existing config.

    Args:
        existing: Existing configuration
        new: New configuration to merge

    Returns:
        Merged configuration
    """
    result = existing.copy()

    for key, value in new.items():
        if key in result and isinstance(result[key], dict) and isinstance(value, dict):
            result[key] = merge_config(result[key], value)
        else:
            result[key] = value

    return result


def load_existing_config(path: Path | None = None) -> dict | None:
    """Load existing configuration if present.

    Args:
        path: Config path to check (uses default if None)

    Returns:
        Parsed config dict, or None if not found
    """
    config_path = path or get_config_path()

    if not config_path.exists():
        return None

    try:
        with config_path.open("rb") as f:
            return tomllib.load(f)
    except Exception:  # noqa: BLE001  # pylint: disable=broad-exception-caught
        return None
