"""Command to initialize or validate .rhiza/template.yml.

This module provides the init command that creates or validates the
.rhiza/template.yml file, which defines where templates come from
and what paths are governed by Rhiza.
"""

import importlib.resources
import keyword
import re
import subprocess  # nosec B404
import sys
import urllib.error
from pathlib import Path

import typer
from jinja2 import Template
from loguru import logger

from rhiza.commands.list_repos import _DESC_WIDTH, _fetch_repos
from rhiza.commands.validate import validate
from rhiza.models import GitContext, GitHost, RhizaTemplate


def _normalize_package_name(name: str) -> str:
    """Normalize a string into a valid Python package name.

    Args:
        name: The input string (e.g., project name).

    Returns:
        A valid Python identifier safe for use as a package name.
    """
    name = re.sub(r"[^a-zA-Z0-9_]", "_", name)
    if name[0].isdigit():
        name = f"_{name}"
    if keyword.iskeyword(name):
        name = f"{name}_"
    return name


def _validate_git_host(git_host: str | None) -> GitHost | None:
    """Validate git_host parameter.

    Args:
        git_host: Git hosting platform.

    Returns:
        Validated GitHost enum value or None.

    Raises:
        ValueError: If git_host is invalid.
    """
    if git_host is not None:
        git_host = git_host.lower()
        if git_host not in GitHost._value2member_map_:
            logger.error(f"Invalid git-host: {git_host}. Must be 'github' or 'gitlab'")
            raise ValueError(f"Invalid git-host: {git_host}. Must be 'github' or 'gitlab'")  # noqa: TRY003
        return GitHost(git_host)
    return None


def _check_template_repository_reachable(template_repository: str, git_host: GitHost | str = GitHost.GITHUB) -> bool:
    """Check if the template repository is reachable via git ls-remote.

    Args:
        template_repository: Repository in 'owner/repo' format.
        git_host: Git hosting platform ('github' or 'gitlab'). Defaults to 'github'.

    Returns:
        True if the repository is reachable, False otherwise.
    """
    host_urls = {
        GitHost.GITHUB: "https://github.com",
        GitHost.GITLAB: "https://gitlab.com",
    }
    base_url = host_urls.get(git_host, "https://github.com")
    repo_url = f"{base_url}/{template_repository}"

    logger.debug(f"Checking reachability of template repository: {repo_url}")
    try:
        git_ctx = GitContext.default()
        result = subprocess.run(  # nosec B603  # noqa: S603
            [git_ctx.executable, "ls-remote", "--exit-code", repo_url],
            capture_output=True,
            timeout=30,
            env=git_ctx.env,
        )
        if result.returncode == 0:
            logger.success(f"Template repository is reachable: {template_repository}")
            return True
        else:
            logger.error(f"Template repository '{template_repository}' is not accessible at {repo_url}")
            logger.error("Please check that the repository exists and you have access to it.")
            return False
    except subprocess.TimeoutExpired:
        logger.error(f"Timed out while checking repository reachability: {repo_url}")
        logger.error("Please check your network connection and try again.")
        return False
    except RuntimeError as e:
        logger.warning(f"Could not verify template repository reachability: {e}")
        return True  # Don't block init if git is unavailable


def _prompt_git_host() -> GitHost:
    """Prompt user for git hosting platform.

    Returns:
        Git hosting platform choice as a GitHost enum value.
    """
    if sys.stdin.isatty():
        logger.info("Where will your project be hosted?")
        git_host = typer.prompt(
            "Target Git hosting platform (github/gitlab)",
            type=str,
            default="github",
        ).lower()

        while git_host not in GitHost._value2member_map_:
            logger.warning(f"Invalid choice: {git_host}. Please choose 'github' or 'gitlab'")
            git_host = typer.prompt(
                "Target Git hosting platform (github/gitlab)",
                type=str,
                default="github",
            ).lower()
    else:
        git_host = "github"
        logger.debug("Non-interactive mode detected, defaulting to github")

    return GitHost(git_host)


def _prompt_template_repository() -> str | None:
    """Prompt the user to select a template repository from a list of rhiza-tagged repos.

    Fetches repositories tagged with 'rhiza' from the GitHub API and presents
    them as a numbered list. In non-interactive or offline scenarios the function
    returns None so the caller falls back to the language default.

    Returns:
        The selected repository in 'owner/repo' format, or None if the user
        accepts the default or selection is not possible.
    """
    if not sys.stdin.isatty():
        logger.debug("Non-interactive mode detected, skipping template repository selection")
        return None

    try:
        repos = _fetch_repos()
    except urllib.error.URLError as exc:
        logger.debug(f"Could not fetch repository list: {exc}")
        return None

    if not repos:
        return None

    # Display a compact numbered list
    typer.echo("\nAvailable template repositories:")
    for i, repo in enumerate(repos, start=1):
        desc = repo.description[:_DESC_WIDTH] if repo.description else ""
        typer.echo(f"  {i:>2}  {repo.full_name:<30}  {desc}")

    typer.echo("")
    selection = typer.prompt(
        "Select a template repository by number, or press Enter to use the default",
        default="",
    ).strip()

    if not selection:
        return None

    try:
        idx = int(selection)
        if 1 <= idx <= len(repos):
            chosen = repos[idx - 1].full_name
            logger.info(f"Selected template repository: {chosen}")
            return chosen
        else:
            logger.warning(f"Invalid selection '{idx}', using default repository")
            return None
    except ValueError:
        logger.warning(f"Invalid input '{selection}', using default repository")
        return None


def _get_default_templates_for_host(git_host: GitHost | str) -> list[str]:
    """Get default templates based on git hosting platform.

    Args:
        git_host: Git hosting platform.

    Returns:
        List of template names.
    """
    common = ["core", "tests", "book", "marimo", "presentation"]
    if git_host == GitHost.GITLAB:
        return [*common, "gitlab"]
    else:
        return [*common, "github"]


def _create_template_file(
    target: Path,
    git_host: GitHost | str,
    language: str = "python",
    template_repository: str | None = None,
    template_branch: str | None = None,
) -> None:
    """Create default template.yml file.

    Args:
        target: Target repository path.
        git_host: Git hosting platform.
        language: Programming language for the project (default: python).
        template_repository: Custom template repository (format: owner/repo).
        template_branch: Custom template branch.
    """
    rhiza_dir = target / ".rhiza"
    template_file = rhiza_dir / "template.yml"

    if template_file.exists():
        return

    logger.info("Creating default .rhiza/template.yml")
    logger.debug("Using default template configuration")

    # Use custom template repository/branch if provided, otherwise use language defaults
    if template_repository:
        repo = template_repository
        logger.info(f"Using custom template repository: {repo}")
    else:
        # Default repositories by language
        repo = "jebel-quant/rhiza-go" if language == "go" else "jebel-quant/rhiza"
        logger.debug(f"Using default repository for {language}: {repo}")

    branch = template_branch or "main"

    # Log when custom values are used
    if template_branch:
        logger.info(f"Using custom template branch: {branch}")

    templates = _get_default_templates_for_host(git_host)
    logger.info(f"Using template-based configuration with templates: {', '.join(templates)}")
    default_template = RhizaTemplate(
        template_repository=repo,
        template_branch=branch,
        language=language,
        templates=templates,
    )

    logger.debug(f"Writing default template to: {template_file}")
    default_template.to_yaml(template_file)

    logger.success("✓ Created .rhiza/template.yml")
    logger.info("""
Next steps:
  1. Review and customize .rhiza/template.yml to match your project needs
  2. Run 'uvx rhiza sync' to inject templates into your repository
""")


def _create_python_package(target: Path, project_name: str, package_name: str) -> None:
    """Create basic Python package structure.

    Args:
        target: Target repository path.
        project_name: Project name.
        package_name: Package name.
    """
    src_folder = target / "src" / package_name
    test_folder = target / "tests"

    if (target / "src").exists():
        return

    logger.info(f"Creating Python package structure: {src_folder}")
    src_folder.mkdir(parents=True)

    logger.info(f"Creating test folder: {test_folder}")
    test_folder.mkdir(parents=True)

    # Create __init__.py
    init_file = src_folder / "__init__.py"
    logger.debug(f"Creating {init_file}")
    init_file.touch()

    template_content = importlib.resources.files("rhiza").joinpath("_templates/basic/__init__.py.jinja2").read_text()
    template = Template(template_content)
    code = template.render(project_name=project_name)
    init_file.write_text(code)

    # Create main.py
    main_file = src_folder / "main.py"
    logger.debug(f"Creating {main_file} with example code")
    main_file.touch()

    template_content = importlib.resources.files("rhiza").joinpath("_templates/basic/main.py.jinja2").read_text()
    template = Template(template_content)
    code = template.render(project_name=project_name)
    main_file.write_text(code)
    logger.success(f"Created Python package structure in {src_folder}")

    # Create main.py
    test_file = test_folder / "test_main.py"
    logger.debug(f"Creating {test_file} with example code")
    test_file.touch()

    template_content = importlib.resources.files("rhiza").joinpath("_templates/basic/test_main.py.jinja2").read_text()
    template = Template(template_content)
    code = template.render(project_name=project_name)
    test_file.write_text(code)
    # logger.success(f"Created Python package structure in {src_folder}")


def _create_pyproject_toml(target: Path, project_name: str, package_name: str, with_dev_dependencies: bool) -> None:
    """Create pyproject.toml file.

    Args:
        target: Target repository path.
        project_name: Project name.
        package_name: Package name.
        with_dev_dependencies: Whether to include dev dependencies.
    """
    pyproject_file = target / "pyproject.toml"
    if pyproject_file.exists():
        return

    logger.info("Creating pyproject.toml with basic project metadata")
    pyproject_file.touch()

    template_content = importlib.resources.files("rhiza").joinpath("_templates/basic/pyproject.toml.jinja2").read_text()
    template = Template(template_content)
    code = template.render(
        project_name=project_name,
        package_name=package_name,
        with_dev_dependencies=with_dev_dependencies,
    )
    pyproject_file.write_text(code)
    logger.success("Created pyproject.toml")


def _create_readme(target: Path) -> None:
    """Create README.md file.

    Args:
        target: Target repository path.
    """
    readme_file = target / "README.md"
    if readme_file.exists():
        return

    logger.info("Creating README.md")
    readme_file.touch()
    logger.success("Created README.md")


def init(
    target: Path,
    project_name: str | None = None,
    package_name: str | None = None,
    with_dev_dependencies: bool = False,
    git_host: str | None = None,
    language: str = "python",
    template_repository: str | None = None,
    template_branch: str | None = None,
) -> bool:
    """Initialize or validate .rhiza/template.yml in the target repository.

    Creates a default .rhiza/template.yml file if it doesn't exist,
    or validates an existing one.

    Args:
        target: Path to the target directory. Defaults to the current working directory.
        project_name: Custom project name. Defaults to target directory name.
        package_name: Custom package name. Defaults to normalized project name.
        with_dev_dependencies: Include development dependencies in pyproject.toml.
        git_host: Target Git hosting platform ("github" or "gitlab"). Determines which
            CI/CD configuration files to include. If None, will prompt user interactively.
        language: Programming language for the project (default: python).
            Supported: python, go. Determines which project files to create.
        template_repository: Custom template repository (format: owner/repo).
            Defaults to 'jebel-quant/rhiza' for Python or 'jebel-quant/rhiza-go' for Go.
        template_branch: Custom template branch. Defaults to 'main'.

    Returns:
        bool: True if validation passes, False otherwise.
    """
    target = target.resolve()
    git_host = _validate_git_host(git_host)

    logger.info(f"Initializing Rhiza configuration in: {target}")
    logger.info(f"Project language: {language}")

    # Create .rhiza directory
    rhiza_dir = target / ".rhiza"
    logger.debug(f"Ensuring directory exists: {rhiza_dir}")
    rhiza_dir.mkdir(parents=True, exist_ok=True)

    # Determine git host
    if git_host is None:
        git_host = _prompt_git_host()

    # When no template repository is specified and no config file exists yet,
    # offer the user an interactive selection from discovered rhiza repos.
    template_yml = target / ".rhiza" / "template.yml"
    if template_repository is None and not template_yml.exists():
        template_repository = _prompt_template_repository()

    # Validate template repository reachability early if a custom one is specified
    if template_repository is not None and not _check_template_repository_reachable(template_repository, git_host):
        return False

    # Create template file with language
    _create_template_file(target, git_host, language, template_repository, template_branch)

    # Bootstrap project structure based on language
    if language == "python":
        # Python-specific setup
        if project_name is None:
            project_name = target.name
        if package_name is None:
            package_name = _normalize_package_name(project_name)

        logger.debug(f"Project name: {project_name}")
        logger.debug(f"Package name: {package_name}")

        _create_python_package(target, project_name, package_name)
        _create_pyproject_toml(target, project_name, package_name, with_dev_dependencies)
        _create_readme(target)
    elif language == "go":
        # Go-specific setup - just create README, user should run go mod init
        _create_readme(target)
        logger.info("For Go projects, run 'go mod init <module-name>' to initialize the module")
    else:
        # Unknown language - just create README
        logger.warning(f"Unknown language '{language}', creating minimal structure")
        _create_readme(target)

    # Validate the template file
    logger.debug("Validating template configuration")
    return validate(target)
