#!/usr/bin/env python3
# File: scripts/pre_commit_docstring_check.py
# Purpose: Pre-commit hook for tiered docstring compliance checking.
# Context: Runs before git commit to enforce tier-specific docstring standards on staged files.

"""
Pre-commit hook for tiered docstring compliance.

Purpose:
    Check only staged files for docstring compliance.
    Enforcement level depends on file path (Domain strict, others relaxed).
    Blocks commit if Domain layer violations detected.
    Warns (but allows) for other layers.

Returns:
    Exit code 0 if all checks pass, 1 if Domain violations found

Side Effects:
    - Runs comment_header_audit.py on staged Python files
    - Prints audit results to stdout/stderr
    - May block git commit
"""

import subprocess
import sys
from pathlib import Path


def is_domain_layer_file(file_path: str | Path) -> bool:
    """Determine if file is in Domain layer using structural path analysis.

    Purpose:
        Check if a file path belongs to src/noveler/domain/** layer.
        Uses structural path analysis (not string matching) for reliability.

    Args:
        file_path: File path as string or Path object

    Returns:
        True if file is in Domain layer, False otherwise

    Side Effects:
        None
    """
    try:
        parts = Path(file_path).parts
        # Domain layer pattern: ['src', 'noveler', 'domain', ...]
        # Ensure exactly 3+ parts and correct order
        return (len(parts) >= 3 and
                parts[0] == "src" and
                parts[1] == "noveler" and
                parts[2] == "domain")
    except (AttributeError, IndexError, TypeError):
        return False


def get_staged_python_files() -> list[Path]:
    """Extract Python files from git staging area.

    Purpose:
        Get list of Python files staged for commit.

    Returns:
        List of Path objects for staged .py files

    Raises:
        subprocess.CalledProcessError: If git command fails

    Side Effects:
        - Runs git command to query staging area
    """
    result = subprocess.run(
        ["git", "diff", "--cached", "--name-only", "--diff-filter=ACM"],
        capture_output=True,
        text=True,
        check=True
    )
    return [
        Path(f) for f in result.stdout.splitlines()
        if f.endswith(".py")
    ]


def check_docstring_compliance(files: list[Path]) -> int:
    """Run tiered docstring audit on staged files.

    Purpose:
        Execute comment_header_audit.py with --tiered mode on staged files.
        Parse output to determine if critical (Domain) violations exist.

    Args:
        files: List of staged Python file paths

    Returns:
        Exit code: 0 if no Domain violations, 1 if Domain violations found
        (inverted for pre-commit semantics: fail if Domain violations exist)

    Side Effects:
        - Runs comment_header_audit.py subprocess
        - Prints audit output to stdout
    """
    if not files:
        return 0  # No Python files to check

    result = subprocess.run(
        ["python", "scripts/comment_header_audit.py", "--tiered"] +
        [str(f) for f in files],
        check=False, capture_output=True,
        text=True
    )

    # Separate Domain and non-Domain files from input
    domain_files = [f for f in files if is_domain_layer_file(f)]
    other_files = [f for f in files if not is_domain_layer_file(f)]

    # Parse audit output to identify violations per file
    # Output format: "<file_path>:<lineno>: <message>"
    domain_violations = []
    other_violations = []

    for line in result.stdout.split("\n"):
        if not line or "Total findings" in line or "Comment audit" in line:
            continue

        # Extract file path from violation line (before first colon)
        try:
            file_path_str = line.split(":")[0].strip()
            if not file_path_str:
                continue

            # Classify based on our structural analysis
            if is_domain_layer_file(file_path_str):
                domain_violations.append(line)
            else:
                other_violations.append(line)
        except (IndexError, AttributeError):
            # If format is unexpected, include in other_violations
            other_violations.append(line)

    # Print audit results
    if result.stdout:
        print(result.stdout)

    # Domain violations should block commit
    if domain_violations:
        print("\n⚠️  Domain layer violations detected! Commit blocked.")
        print(f"   Affected files ({len(domain_files)}):")
        for f in domain_files[:5]:  # Show first 5
            print(f"     - {f}")
        if len(domain_files) > 5:
            print(f"     ... and {len(domain_files) - 5} more")
        print("   Run: python scripts/generate_docstring_templates.py --tiered <file>")
        return 1

    # Other violations just warn
    if other_violations:
        print("\n⚠️  Other layer violations detected (non-blocking).")
        print(f"   Affected files ({len(other_files)}):")
        for f in other_files[:5]:  # Show first 5
            print(f"     - {f}")
        if len(other_files) > 5:
            print(f"     ... and {len(other_files) - 5} more")
        print("   Consider: python scripts/generate_docstring_templates.py --tiered <file>")

    return 0


def main() -> int:
    """Entry point for pre-commit hook.

    Purpose:
        Orchestrate pre-commit checking:
        1. Get staged Python files
        2. Run tiered docstring audit
        3. Block commit if Domain violations found

    Returns:
        Exit code (0 = pass, 1 = fail - blocks commit)

    Side Effects:
        - Reads git staging area
        - Runs subprocess for audit
        - Prints to stdout/stderr
    """
    try:
        files = get_staged_python_files()
        return check_docstring_compliance(files)
    except subprocess.CalledProcessError as e:
        print(f"❌ Error running git command: {e}")
        return 1
    except FileNotFoundError as e:
        print(f"❌ Script not found: {e}")
        return 1
    except Exception as e:
        print(f"❌ Unexpected error: {e}")
        return 1


if __name__ == "__main__":
    sys.exit(main())
