"""Configuration file generation for fnb initialization.

This module handles the generation of starter configuration files from templates,
providing users with ready-to-customize fnb configurations for their backup workflows.

Key features:
- Template-based file generation with flexible discovery
- Optional text replacements and header customization
- Safety checks to prevent accidental overwrites
- Support for both config and environment file generation

Usage patterns:
    fnb init [--force] [--no-env]
    fnb init config [--force]
    fnb init env [--force]
"""

import importlib.resources
import sys
from enum import Enum
from pathlib import Path
from datetime import datetime

import typer


class ConfigKind(str, Enum):
    """Types of configuration files that can be generated.

    This enum inherits from str to all:
    1. Direct string comparison (kind == "all")
    2. Automatic string conversion (str(kind))
    3. Easy conversion from string input (ConfigKind("all"))
    """

    ALL = "all"
    CONFIG = "config"
    ENV = "env"


def find_template(template_name: str) -> Path | None:
    """Locate a template file by searching multiple possible locations.

    Implements a flexible template discovery system that works in both
    development and installed package environments. Searches through
    multiple locations in priority order to find the requested template.

    Args:
        template_name: Name of the template file to locate (e.g., "config.toml").

    Returns:
        Path | None: Path to the first template file found in the search order,
        or None if the template doesn't exist in any location.

    Examples:
        Find config template:
        
        >>> path = find_template("config.toml")
        >>> path.name
        'config.toml'
        
        Template not found:
        
        >>> path = find_template("nonexistent.toml")
        >>> path is None
        True
        
        Find environment template:
        
        >>> path = find_template("env.sample")
        >>> path.exists()
        True
    """
    # Try multiple possible paths to find the template
    possible_sources = [
        importlib.resources.files("fnb.assets").joinpath(
            template_name
        ),  # Installed package
        Path(__file__).parent.parent
        / "assets"
        / template_name,  # Relative to this file
        Path(f"src/fnb/assets/{template_name}"),  # Project root in dev
    ]

    return next((p for p in possible_sources if p.exists()), None)


def create_file_from_template(
    template_name: str,
    dest_path: Path,
    force: bool = False,
    replacements: dict[str, str] | None = None,
    header_comment: str | None = None,
) -> bool:
    """Generate a file from a template with optional content customization.

    Creates a new file by copying from a template and applying optional text
    replacements and header comments. Provides safety checks to prevent
    accidental overwrites and handles template processing errors gracefully.

    Args:
        template_name: Name of the template file to use as source.
        dest_path: Path where the generated file should be created.
        force: If True, overwrites existing files without prompting.
            If False, aborts if destination file already exists.
        replacements: Optional dictionary of string replacements to apply
            to template content. Keys are old strings, values are replacements.
        header_comment: Optional comment to prepend to the generated file,
            useful for adding generation timestamps or custom headers.

    Returns:
        bool: True if file was created successfully, False if operation
        failed due to template not found, file conflicts, or I/O errors.

    Examples:
        Basic file generation:
        
        >>> success = create_file_from_template(
        ...     "config.toml", Path("./fnb.toml")
        ... )
        ✅ Created ./fnb.toml from template.
        >>> success
        True
        
        Generate with replacements:
        
        >>> replacements = {"enabled = true": "enabled = false"}
        >>> success = create_file_from_template(
        ...     "config.toml", Path("./fnb.toml"), 
        ...     replacements=replacements
        ... )
        >>> success
        True
        
        Generate with header comment:
        
        >>> header = "# Generated by fnb init at 2024-01-01"
        >>> success = create_file_from_template(
        ...     "env.sample", Path("./.env"), 
        ...     header_comment=header
        ... )
        >>> success
        True
        
        Handle existing file (no force):
        
        >>> success = create_file_from_template(
        ...     "config.toml", Path("./existing.toml"), force=False
        ... )
        ❌ ./existing.toml already exists. Use --force to overwrite.
        >>> success
        False
        
        Force overwrite existing file:
        
        >>> success = create_file_from_template(
        ...     "config.toml", Path("./existing.toml"), force=True
        ... )
        ✅ Created ./existing.toml from template.
        >>> success
        True
    """
    if dest_path.exists() and not force:
        typer.echo(f"❌ {dest_path} already exists. Use --force to overwrite.")
        return False

    # Find the template
    src = find_template(template_name)
    if not src:
        typer.echo(
            f"❌ Template file {template_name} not found. Check if fnb is installed correctly."
        )
        return False

    try:
        # Create parent directories if needed
        dest_path.parent.mkdir(parents=True, exist_ok=True)

        # Read the template
        with src.open("r") as source_file:
            content = source_file.read()

        # Add header comment if provided
        if header_comment and not content.startswith(header_comment):
            content = f"{header_comment}\n\n{content}"

        # Apply replacements if provided
        if replacements:
            for old, new in replacements.items():
                content = content.replace(old, new)

        # Write the modified content
        with dest_path.open("w") as dest_file:
            dest_file.write(content)

        typer.echo(f"✅ Created {dest_path} from template.")
        return True

    except Exception as e:
        typer.echo(f"❌ Error creating file: {e}")
        return False


def create_config_file(force: bool = False) -> bool:
    """Create a default fnb.toml file in the current directory.

    Args:
        force (bool): If True, overwrite existing file without confirmation.

    Returns:
        bool: True if successful, False otherwise.
    """
    dest = Path("./fnb.toml")

    header_comment = (
        "# >>>>> Generated by fnb init",
        f"# >>>>> At {datetime.now().isoformat()}",
    )
    replacements = {"enabled = true": "enabled = false  # Set to true once configured"}

    success = create_file_from_template(
        template_name="config.toml",
        dest_path=dest,
        force=force,
        replacements=replacements,
        header_comment=("\n").join(header_comment),
    )

    if success:
        typer.echo("Edit this file to configure your backup tasks.")

    return success


def create_env_file(force: bool = False) -> bool:
    """Create a .env.plain file from the sample template.

    Args:
        force (bool): If True, overwrite existing file without confirmation.

    Returns:
        bool: True if successful, False otherwise.
    """
    dest = Path("./.env.plain")

    header_comment = (
        "# >>>>> Generated by fnb init",
        f"# >>>>> At {datetime.now().isoformat()}",
    )

    success = create_file_from_template(
        template_name="env.sample",
        dest_path=dest,
        force=force,
        header_comment=("\n").join(header_comment),
    )

    if success:
        typer.echo("Edit this file to configure your SSH passwords.")

    return success


def run(kind: ConfigKind = ConfigKind.ALL, force: bool = False) -> None:
    """CLI entry point for config file generation.

    Args:
        kind (str): Kind of configuration file to generate.
        force (bool): If True, overwrite existing file without confirmation.
    """
    try:
        config_kind = ConfigKind(kind.lower())
    except ValueError:
        typer.echo(f"❌ Invalid configuration kind: {kind}")
        typer.echo("Valid kinds: all, config, env")
        sys.exit(1)

    # Track overall success
    success = True

    # Generate config.toml if requested
    if config_kind in [ConfigKind.ALL, ConfigKind.CONFIG]:
        config_success = create_config_file(force)
        success = success and config_success

    # Generate .env if requested
    if config_kind in [ConfigKind.ALL, ConfigKind.ENV]:
        env_success = create_env_file(force)
        success = success and env_success

    # Exit with error if any file creation failed
    if not success:
        sys.exit(1)


if __name__ == "__main__":
    """Self test.

    $ uv run src/fnb/generator.py
    """
    run(kind=ConfigKind.ALL, force=True)
