"""
core/git_scan.py — VaultHound v1.0.0
Git history scanner — walks commits looking for removed/added secrets.
"""

from __future__ import annotations

import subprocess
from pathlib import Path

from core.scanner import Scanner
from utils.logger import safe_print, log
from utils.models import SecretFinding


def _run_git(args: list[str], cwd: Path) -> str:
    """Run a git command in cwd. Returns stdout or empty string on error."""
    try:
        result = subprocess.run(
            ["git"] + args,
            cwd=cwd,
            capture_output=True,
            text=True,
            timeout=60,
        )
        return result.stdout
    except (subprocess.TimeoutExpired, FileNotFoundError, OSError) as e:
        log.warning(f"git error: {e}")
        return ""


def _is_git_repo(path: Path) -> bool:
    return (path / ".git").exists() or _run_git(["rev-parse", "--git-dir"], path).strip() != ""


def scan_git_history(
    target_dir: str,
    scanner:    Scanner,
    max_commits: int = 200,
) -> tuple[list[SecretFinding], int]:
    """
    Walk git history and scan each commit diff for secrets.
    Catches secrets that were committed then deleted.

    Returns:
        (findings, commits_scanned)
    """
    root = Path(target_dir)

    if not _is_git_repo(root):
        safe_print(f"[warning]  No git repository found at {target_dir}[/]")
        return [], 0

    # Get commit hashes
    log_output = _run_git(["log", "--format=%H", f"-{max_commits}"], root)
    commits = [h.strip() for h in log_output.splitlines() if h.strip()]

    if not commits:
        safe_print("[warning]  No git commits found[/]")
        return [], 0

    safe_print(f"[info]  Scanning {len(commits)} git commit(s)...[/]")

    all_findings: list[SecretFinding] = []

    for i, commit_hash in enumerate(commits):
        # Get diff for this commit
        diff = _run_git(
            ["show", commit_hash, "--unified=0", "--no-color"],
            root
        )
        if not diff:
            continue

        # Extract only added lines from diff (+lines)
        added_lines = []
        current_file = f"git:{commit_hash[:8]}"
        line_num = 0

        for line in diff.splitlines():
            if line.startswith("diff --git"):
                # Extract filename
                parts = line.split(" b/")
                if len(parts) > 1:
                    current_file = f"git:{commit_hash[:8]}:{parts[-1]}"
            elif line.startswith("@@"):
                # Extract line number from @@ -X,Y +A,B @@
                try:
                    plus_part = line.split("+")[1].split(",")[0].split(" ")[0]
                    line_num = int(plus_part)
                except (IndexError, ValueError):
                    line_num = 0
            elif line.startswith("+") and not line.startswith("+++"):
                added_lines.append((line_num, line[1:]))  # strip leading +
                line_num += 1

        # Scan added lines
        if added_lines:
            content = "\n".join(line for _, line in added_lines)
            findings = scanner.scan_text(
                content,
                source=current_file,
                start_line=1,
            )
            if findings:
                all_findings.extend(findings)
                short = commit_hash[:8]
                safe_print(
                    f"  [danger]✘ {len(findings)} secret(s)[/] in "
                    f"[dim]commit {short}[/]"
                )

        # Progress every 50 commits
        if (i + 1) % 50 == 0:
            safe_print(f"[dim]  ... {i + 1}/{len(commits)} commits scanned[/]")

    return all_findings, len(commits)
