"""Scaffold minimal fixture repositories."""

from __future__ import annotations

import argparse
import shutil
from pathlib import Path

from cihub.exit_codes import EXIT_FAILURE, EXIT_SUCCESS, EXIT_USAGE
from cihub.types import CommandResult
from cihub.utils.paths import hub_root
from cihub.utils.github import (
    check_gh_auth,
    check_repo_exists,
    create_github_repo,
    get_gh_username,
    git_init_and_commit,
)

SCAFFOLD_TYPES: dict[str, str] = {
    "python-pyproject": "Minimal Python project with pyproject.toml",
    "python-setup": "Minimal Python project with setup.py",
    "python-src-layout": "Python project with src/ directory layout",
    "java-maven": "Minimal Java project using Maven",
    "java-gradle": "Minimal Java project using Gradle",
    "java-multi-module": "Multi-module Maven project with parent POM",
    "monorepo": "Monorepo with java/ and python/ subdirs",
}


def list_scaffold_types() -> list[dict[str, str]]:
    return [{"type": name, "description": desc} for name, desc in SCAFFOLD_TYPES.items()]


def _template_root() -> Path:
    return hub_root() / "templates" / "scaffold"


def _ensure_empty(path: Path, force: bool) -> None:
    if path.exists() and path.is_file():
        raise ValueError(f"Destination is a file: {path}")
    if path.exists() and any(path.iterdir()):
        if not force:
            raise ValueError(f"Destination is not empty: {path}")
        shutil.rmtree(path)
    path.mkdir(parents=True, exist_ok=True)


def _copy_tree(src: Path, dest: Path) -> None:
    shutil.copytree(src, dest, dirs_exist_ok=True)


def _list_files(root: Path) -> list[str]:
    return [str(path) for path in sorted(root.rglob("*")) if path.is_file()]


def scaffold_fixture(fixture_type: str, dest: Path, force: bool = False) -> list[str]:
    if fixture_type not in SCAFFOLD_TYPES:
        raise ValueError(f"Unknown fixture type: {fixture_type}")

    template_root = _template_root()
    _ensure_empty(dest, force)

    if fixture_type == "monorepo":
        (dest / "README.md").write_text(
            "Monorepo scaffold generated by cihub.\n",
            encoding="utf-8",
        )
        _copy_tree(template_root / "java-maven", dest / "java")
        _copy_tree(template_root / "python-pyproject", dest / "python")
    else:
        template_dir = template_root / fixture_type
        if not template_dir.exists():
            raise ValueError(f"Missing scaffold template: {template_dir}")
        _copy_tree(template_dir, dest)

    return _list_files(dest)


def _get_language_for_scaffold(fixture_type: str) -> str:
    """Determine language based on scaffold type."""
    if fixture_type.startswith("python"):
        return "python"
    if fixture_type.startswith("java"):
        return "java"
    # monorepo defaults to java (has both)
    return "java"


def _run_init_for_scaffold(dest: Path, fixture_type: str) -> CommandResult:
    """Run cihub init for the scaffolded project."""
    from cihub.commands.init import cmd_init

    language = _get_language_for_scaffold(fixture_type)

    # Create a namespace that mimics init args
    init_args = argparse.Namespace(
        repo=str(dest),
        language=language,
        owner=None,
        name=None,
        branch="main",
        subdir=None,
        fix_pom=False,
        apply=True,
        force=True,
        wizard=False,
        dry_run=False,
        json=False,
    )
    return cmd_init(init_args)


def cmd_scaffold(args: argparse.Namespace) -> CommandResult:
    """Scaffold a minimal fixture repository.

    Always returns CommandResult for consistent output handling.
    """
    if args.list:
        data = {"fixtures": list_scaffold_types()}
        items = [f"{entry['type']}: {entry['description']}" for entry in data["fixtures"]]
        return CommandResult(
            exit_code=EXIT_SUCCESS,
            summary="Available fixtures",
            data={"items": items, "fixtures": data["fixtures"]},
        )

    fixture_type = args.type
    dest = Path(args.path or "").resolve()

    if not fixture_type or not args.path:
        message = "type and path are required unless --list is used"
        return CommandResult(
            exit_code=EXIT_USAGE,
            summary=message,
            problems=[{"severity": "error", "message": message, "code": "CIHUB-SCAFFOLD-001"}],
        )

    # Check GitHub prerequisites early if --github
    use_github = getattr(args, "github", False)
    if use_github:
        auth_ok, auth_msg = check_gh_auth()
        if not auth_ok:
            return CommandResult(
                exit_code=EXIT_FAILURE,
                summary=auth_msg,
                problems=[{"severity": "error", "message": auth_msg, "code": "CIHUB-SCAFFOLD-GH-AUTH"}],
                suggestions=[{"message": "Run: gh auth login"}],
            )

        # Check if repo already exists
        repo_name = getattr(args, "repo_name", None) or dest.name
        owner = get_gh_username()
        if owner and check_repo_exists(owner, repo_name):
            msg = f"Repository '{owner}/{repo_name}' already exists on GitHub"
            return CommandResult(
                exit_code=EXIT_FAILURE,
                summary=msg,
                problems=[{"severity": "error", "message": msg, "code": "CIHUB-SCAFFOLD-GH-EXISTS"}],
                suggestions=[
                    {"message": f"Use --repo-name to specify a different name"},
                    {"message": f"Or delete the existing repo: gh repo delete {owner}/{repo_name}"},
                ],
            )

    # Scaffold the project
    try:
        files = scaffold_fixture(fixture_type, dest, force=bool(args.force))
    except ValueError as exc:
        return CommandResult(
            exit_code=EXIT_FAILURE,
            summary=str(exc),
            problems=[{"severity": "error", "message": str(exc), "code": "CIHUB-SCAFFOLD-002"}],
        )

    # If not using GitHub, return early
    if not use_github:
        summary = f"Scaffolded {fixture_type} at {dest}"
        return CommandResult(
            exit_code=EXIT_SUCCESS,
            summary=summary,
            files_generated=files,
            data={
                "items": [summary],
                "type": fixture_type,
                "path": str(dest),
                "files": files,
            },
        )

    # GitHub flow: init + git + push
    no_init = getattr(args, "no_init", False)
    no_push = getattr(args, "no_push", False)
    private = getattr(args, "private", False)
    repo_name = getattr(args, "repo_name", None) or dest.name

    # Run init to add CI config (unless --no-init)
    if not no_init:
        init_result = _run_init_for_scaffold(dest, fixture_type)
        if init_result.exit_code != EXIT_SUCCESS:
            return CommandResult(
                exit_code=EXIT_FAILURE,
                summary=f"Failed to initialize CI config: {init_result.summary}",
                problems=init_result.problems,
            )
        # Add init-generated files to our list
        if init_result.files_generated:
            files.extend(init_result.files_generated)

    # Initialize git and commit
    git_ok, git_msg = git_init_and_commit(
        dest,
        message=f"Initial commit: {fixture_type} scaffold from cihub",
    )
    if not git_ok:
        return CommandResult(
            exit_code=EXIT_FAILURE,
            summary=git_msg,
            problems=[{"severity": "error", "message": git_msg, "code": "CIHUB-SCAFFOLD-GIT-INIT"}],
        )

    # Create GitHub repo and push
    description = SCAFFOLD_TYPES.get(fixture_type, "")
    success, repo_url, error = create_github_repo(
        dest,
        repo_name,
        private=private,
        push=not no_push,
        description=f"CI/CD Hub test: {description}",
    )
    if not success:
        return CommandResult(
            exit_code=EXIT_FAILURE,
            summary=error,
            problems=[{"severity": "error", "message": error, "code": "CIHUB-SCAFFOLD-GH-CREATE"}],
        )

    summary = f"Scaffolded {fixture_type} and created GitHub repo: {repo_url}"
    return CommandResult(
        exit_code=EXIT_SUCCESS,
        summary=summary,
        files_generated=files,
        data={
            "items": [summary],
            "type": fixture_type,
            "path": str(dest),
            "github_url": repo_url,
            "files": files,
        },
    )
