"""Built-in regex secret scanner — no external tools required.

Detects hardcoded secrets, API keys, passwords, and tokens using
pattern matching.  This scanner always runs as a fallback so that
SecAgent produces results even without Gitleaks or Trivy installed.
"""

from __future__ import annotations

import logging
import re
from pathlib import Path
from typing import Any

from secagent.scanners.base_scanner import BaseScanner

logger = logging.getLogger(__name__)

# ── Secret patterns ──────────────────────────────────────────────────
_PATTERNS: list[tuple[str, str, re.Pattern[str]]] = [
    (
        "aws-access-key",
        "AWS Access Key ID",
        re.compile(r"(?:AKIA|ABIA|ACCA|ASIA)[0-9A-Z]{16}", re.IGNORECASE),
    ),
    (
        "aws-secret-key",
        "AWS Secret Access Key",
        re.compile(
            r"""(?:aws)?_?(?:secret)?_?(?:access)?_?key\s*[=:]\s*['"]?([A-Za-z0-9/+=]{40})['"]?""",
            re.IGNORECASE,
        ),
    ),
    (
        "generic-api-key",
        "Hardcoded API key",
        re.compile(
            r"""(?:api[_-]?key|apikey|api[_-]?secret)\s*[=:]\s*['"]([^'"]{8,})['"]""",
            re.IGNORECASE,
        ),
    ),
    (
        "generic-secret",
        "Hardcoded secret / password",
        re.compile(
            r"""(?:password|passwd|pwd|secret|token|auth)\s*[=:]\s*['"]([^'"]{6,})['"]""",
            re.IGNORECASE,
        ),
    ),
    (
        "private-key",
        "Private key detected",
        re.compile(r"-----BEGIN (?:RSA |EC |DSA |OPENSSH )?PRIVATE KEY-----"),
    ),
    (
        "github-token",
        "GitHub personal access token",
        re.compile(r"ghp_[A-Za-z0-9]{36}"),
    ),
    (
        "generic-bearer",
        "Hardcoded Bearer token",
        re.compile(
            r"""[Bb]earer\s+['"]?([A-Za-z0-9\-_.~+/]{20,})['"]?"""
        ),
    ),
    (
        "connection-string",
        "Hardcoded database connection string",
        re.compile(
            r"""(?:postgresql|mysql|mongodb|redis|amqp)://\S+:\S+@""",
            re.IGNORECASE,
        ),
    ),
]

# Files / directories to skip
_SKIP_DIRS = {".git", "__pycache__", "node_modules", ".venv", "venv", ".env.example"}
_SKIP_EXTENSIONS = {".pyc", ".pyo", ".so", ".dll", ".exe", ".bin", ".png", ".jpg", ".gif", ".ico"}


class BuiltinSecretScanner(BaseScanner):
    """Regex-based secret scanner — always available, no install required."""

    @property
    def name(self) -> str:
        return "builtin-secrets"

    def is_available(self) -> bool:
        """Always available — pure Python, no external binary."""
        return True

    def run(self, target: Path) -> list[dict[str, Any]]:
        """Scan *target* for hardcoded secrets using regex patterns."""
        findings: list[dict[str, Any]] = []

        files = self._collect_files(target)
        for file_path in files:
            try:
                content = file_path.read_text(encoding="utf-8", errors="replace")
            except OSError:
                continue

            for line_num, line in enumerate(content.splitlines(), start=1):
                stripped = line.strip()
                # Skip comments-only lines that look like documentation
                if stripped.startswith("#") and "example" in stripped.lower():
                    continue

                for rule_id, description, pattern in _PATTERNS:
                    if pattern.search(line):
                        findings.append(
                            {
                                "RuleID": rule_id,
                                "Description": description,
                                "File": str(file_path),
                                "StartLine": line_num,
                                "Match": line.strip(),
                            }
                        )

        logger.info("Built-in scanner found %d potential secret(s).", len(findings))
        return findings

    @staticmethod
    def _collect_files(target: Path) -> list[Path]:
        """Recursively collect scannable text files."""
        if target.is_file():
            if target.suffix not in _SKIP_EXTENSIONS:
                return [target]
            return []

        results: list[Path] = []
        for p in sorted(target.rglob("*")):
            if any(skip in p.parts for skip in _SKIP_DIRS):
                continue
            if p.is_file() and p.suffix not in _SKIP_EXTENSIONS:
                results.append(p)
        return results
