"""Implementation of the `strura init` command."""

import shutil
import subprocess
from pathlib import Path

import typer

from strura.templates.init_templates import (
    CONFTEST_PY,
    ENV_PY,
    INIT_DB_PY,
    MAIN_PY,
)

# Directories to create (relative to project root)
DIRECTORIES: list[str] = [
    "app",
    "app/contexts",
    "app/settings",
    "app/shared/application",
    "app/shared/domain",
    "app/shared/infrastructure",
    "app/shared/utils",
    "app/utils",
    "tests",
    "docs",
]

# Files to generate: (relative_path, content)
# Only created if they don't already exist.
FILES: list[tuple[str, str]] = [
    ("app/__init__.py", ""),
    ("app/main.py", MAIN_PY),
    ("app/contexts/__init__.py", ""),
    ("app/settings/__init__.py", ""),
    ("app/settings/env.py", ENV_PY),
    ("app/settings/init_db.py", INIT_DB_PY),
    ("app/shared/application/__init__.py", ""),
    ("app/shared/domain/__init__.py", ""),
    ("app/shared/infrastructure/__init__.py", ""),
    ("app/shared/utils/__init__.py", ""),
    ("app/utils/__init__.py", ""),
    ("tests/__init__.py", ""),
    ("tests/conftest.py", CONFTEST_PY),
]


def _check_uv_installed() -> None:
    """Verify that `uv` is available in the system PATH."""
    if shutil.which("uv") is None:
        typer.echo(
            "Error: 'uv' is not installed or not found in PATH.\n"
            "   Install it from: https://docs.astral.sh/uv/getting-started/installation/",
            err=True,
        )
        raise typer.Exit(code=1)


def _run_uv_init(name: str, cwd: Path | None = None) -> None:
    """Execute `uv init` as a subprocess."""
    cmd = ["uv", "init", name]
    try:
        subprocess.run(cmd, check=True, cwd=cwd)
    except subprocess.CalledProcessError as exc:
        typer.echo(f"Error: `uv init {name}` failed (exit code {exc.returncode}).", err=True)
        raise typer.Exit(code=1) from exc


def _generate_structure(target: Path) -> None:
    """Create directories and template files inside *target*."""
    # Create directories
    for directory in DIRECTORIES:
        dir_path = target / directory
        dir_path.mkdir(parents=True, exist_ok=True)

    # Move root main.py if it exists and app/main.py does not
    root_main = target / "main.py"
    app_main = target / "app" / "main.py"
    if root_main.exists() and not app_main.exists():
        typer.echo("  Moving existing main.py to app/main.py")
        shutil.move(str(root_main), str(app_main))

    # Create files (skip if they already exist)
    created_count = 0
    skipped_count = 0
    for relative_path, content in FILES:
        file_path = target / relative_path
        if file_path.exists():
            skipped_count += 1
            typer.echo(f"  Skipped (exists): {relative_path}")
        else:
            file_path.parent.mkdir(parents=True, exist_ok=True)
            file_path.write_text(content, encoding="utf-8")
            created_count += 1
            typer.echo(f"  Created: {relative_path}")

    typer.echo(f"\nSummary: {created_count} files created, {skipped_count} files skipped.")


def init_project(name: str) -> None:
    """Entry point for the init command logic.

    Args:
        name: Project name or '.' to format the current directory.
    """
    if name == ".":
        # Mode B: Format existing project (no uv init needed)
        target = Path.cwd()

        if not (target / "pyproject.toml").exists():
            typer.echo(
                "Error: No 'pyproject.toml' found in the current directory.\n"
                "   Run 'uv init' first or use 'strura init <project-name>' to create a new project.",
                err=True,
            )
            raise typer.Exit(code=1)

        typer.echo(f"Formatting existing project in: {target.name}/")
    else:
        # Mode A: Create new project — requires uv
        _check_uv_installed()
        target = Path.cwd() / name

        if target.exists():
            typer.echo(
                f"Error: Directory '{name}' already exists.\n"
                "   Choose a different name or use 'strura init .' inside the existing project.",
                err=True,
            )
            raise typer.Exit(code=1)

        typer.echo(f"Creating new project: {name}")
        _run_uv_init(name)

    # Step 2: Generate DDD structure
    typer.echo("\nGenerating project structure...\n")
    _generate_structure(target)

    typer.echo(f"\nDone! Project '{target.name}' is ready.")
    if name != ".":
        typer.echo(f"   cd {name} && uv sync")
