"""Input validation utilities for CLI."""

from __future__ import annotations

import re
from pathlib import Path


class InputValidator:
    """Validates and sanitizes user input for CLI operations."""

    # Safe branch name pattern - alphanumeric, hyphens, underscores, slashes, dots
    BRANCH_NAME_PATTERN = re.compile(r"^[a-zA-Z0-9/_.-]+$")

    # Safe config key pattern - dot notation with alphanumeric keys
    CONFIG_KEY_PATTERN = re.compile(r"^[a-zA-Z][a-zA-Z0-9_]*(\.[a-zA-Z][a-zA-Z0-9_]*)*$")

    @staticmethod
    def validate_branch_name(name: str | None) -> str | None:
        """Validate and sanitize branch name.

        Args:
            name: Branch name to validate

        Returns:
            Sanitized branch name or None if invalid
        """
        if not name or not name.strip():
            return None

        name = name.strip()

        # Check length
        if len(name) > 250:  # Git ref length limit
            return None

        # Check pattern
        if not InputValidator.BRANCH_NAME_PATTERN.match(name):
            return None

        # Prevent dangerous names
        dangerous_names = {".", "..", "HEAD", "refs", "origin"}
        if name in dangerous_names or name.startswith("."):
            return None

        return name

    @staticmethod
    def validate_worktree_id(worktree_id: str | None) -> str | None:
        """Validate worktree ID.

        Args:
            worktree_id: Worktree ID to validate

        Returns:
            Sanitized worktree ID or None if invalid
        """
        if not worktree_id or not worktree_id.strip():
            return None

        worktree_id = worktree_id.strip()

        # Basic sanitization - remove any control characters
        worktree_id = re.sub(r"[\x00-\x1f\x7f-\x9f]", "", worktree_id)

        if len(worktree_id) > 500:  # Reasonable limit
            return None

        return worktree_id

    @staticmethod
    def validate_config_key(key: str) -> str | None:
        """Validate configuration key.

        Args:
            key: Configuration key to validate

        Returns:
            Sanitized key or None if invalid
        """
        if not key or not key.strip():
            return None

        key = key.strip()

        # Check pattern
        if not InputValidator.CONFIG_KEY_PATTERN.match(key):
            return None

        # Check length
        if len(key) > 100:
            return None

        return key

    @staticmethod
    def validate_config_value(value: str) -> str | None:
        """Validate configuration value.

        Args:
            value: Configuration value to validate

        Returns:
            Sanitized value or None if invalid
        """
        if value is None:
            return None

        value = str(value).strip()

        # Remove control characters
        value = re.sub(r"[\x00-\x1f\x7f-\x9f]", "", value)

        # Length limit
        if len(value) > 1000:
            return None

        return value

    @staticmethod
    def validate_path_safety(path: str) -> bool:
        """Validate that a path is safe for operations.

        Args:
            path: Path to validate

        Returns:
            True if path is safe
        """
        if not path or len(path) > 1000:
            return False

        # Basic validation - rely on Path.resolve() for security
        # This is consistent with existing worktree path validation
        try:
            from pathlib import Path

            resolved = Path(path).resolve()
            return len(str(resolved)) <= 1000
        except (OSError, ValueError):
            return False

    @staticmethod
    def find_project_root(start_path: Path) -> Path | None:
        """Find project root by looking for .git directory or file.

        For regular repositories, this finds the .git directory.
        For worktrees, this follows the .git file to find the main repository.

        Args:
            start_path: Starting directory to search from

        Returns:
            Project root path or None if not found
        """
        current_path = start_path
        while current_path != current_path.parent:
            git_path = current_path / ".git"
            if git_path.exists():
                # Check if it's a worktree (file) or regular repo (directory)
                if git_path.is_file():
                    # It's a worktree - read the gitdir reference
                    try:
                        with open(git_path, "r") as f:
                            gitdir_line = f.read().strip()
                            if gitdir_line.startswith("gitdir: "):
                                # Extract path: "gitdir: /path/to/main/repo/.git/worktrees/name"
                                gitdir = Path(gitdir_line[8:])  # Remove 'gitdir: ' prefix
                                # Go up from .git/worktrees/name to find main repo
                                if gitdir.parts[-2] == "worktrees" and gitdir.parts[-3] == ".git":
                                    # Return the main repository root
                                    return gitdir.parent.parent.parent
                    except (IOError, IndexError):
                        pass
                else:
                    # It's a regular repository
                    return current_path
            current_path = current_path.parent
        return None

    @staticmethod
    def validate_editor_command(command: str) -> str | None:
        """Validate editor command for security.

        Args:
            command: Editor command to validate

        Returns:
            Sanitized command or None if invalid
        """
        if not command or not command.strip():
            return None

        command = command.strip()

        # Only allow known safe editors
        safe_editors = {
            "cursor",
            "code",
            "vim",
            "nvim",
            "nano",
            "emacs",
            "subl",
            "atom",
            "gedit",
            "kate",
        }

        # Extract base command (before any arguments)
        base_command = command.split()[0]

        # Check if it's in safe list
        if base_command in safe_editors:
            return base_command

        # Check if it's a full path to a safe editor
        if "/" in base_command:
            editor_name = Path(base_command).name
            if editor_name in safe_editors:
                return base_command

        return None

    @staticmethod
    def validate_hook_stage(stage: str) -> str | None:
        """Validate hook stage name.

        Args:
            stage: Hook stage to validate

        Returns:
            Validated stage or None if invalid
        """
        if not stage:
            return None

        valid_stages = {"post_create", "pre_delete", "pre_switch", "post_switch"}

        return stage if stage in valid_stages else None
