"""
Security Patterns for Secret Detection.

Comprehensive patterns to detect sensitive information including:
- API keys from major providers
- Passwords and secrets
- Personal identifiable information (PII)
- File paths containing usernames
- Database connection strings
- Private keys and certificates
"""

import re
import json
from dataclasses import dataclass, field
from enum import Enum
from typing import List, Optional, Pattern, Dict, Any
from pathlib import Path


class FindingType(Enum):
    """Types of sensitive findings."""
    API_KEY = "api_key"
    PASSWORD = "password"
    TOKEN = "token"
    PII_EMAIL = "pii_email"
    PII_PHONE = "pii_phone"
    PII_SSN = "pii_ssn"
    PII_CREDIT_CARD = "pii_credit_card"
    FILE_PATH = "file_path"
    PRIVATE_KEY = "private_key"
    CONNECTION_STRING = "connection_string"
    CUSTOM = "custom"


class FindingSeverity(Enum):
    """Severity levels for findings."""
    CRITICAL = "critical"  # Definite secret, block immediately
    HIGH = "high"          # Very likely secret
    MEDIUM = "medium"      # Possible secret, review recommended
    LOW = "low"            # Informational, might be intentional
    INFO = "info"          # Just FYI, probably fine


@dataclass
class PatternRule:
    """A pattern rule for detecting secrets."""
    name: str
    pattern: str
    finding_type: FindingType
    severity: FindingSeverity
    description: str
    compiled: Optional[Pattern] = field(default=None, repr=False)

    def __post_init__(self):
        if self.compiled is None:
            self.compiled = re.compile(self.pattern, re.IGNORECASE)

    def match(self, content: str) -> List[tuple]:
        """Return list of (match_text, start, end) tuples."""
        matches = []
        for m in self.compiled.finditer(content):
            matches.append((m.group(), m.start(), m.end()))
        return matches


# =============================================================================
# BUILT-IN PATTERNS
# =============================================================================

BUILTIN_PATTERNS: List[PatternRule] = [
    # -------------------------------------------------------------------------
    # API KEYS - Major Providers
    # -------------------------------------------------------------------------
    PatternRule(
        name="OpenAI API Key",
        pattern=r"sk-[a-zA-Z0-9]{32,}",
        finding_type=FindingType.API_KEY,
        severity=FindingSeverity.CRITICAL,
        description="OpenAI API key detected",
    ),
    PatternRule(
        name="Anthropic API Key",
        pattern=r"sk-ant-[a-zA-Z0-9\-]{32,}",
        finding_type=FindingType.API_KEY,
        severity=FindingSeverity.CRITICAL,
        description="Anthropic API key detected",
    ),
    PatternRule(
        name="AWS Access Key ID",
        pattern=r"AKIA[0-9A-Z]{16}",
        finding_type=FindingType.API_KEY,
        severity=FindingSeverity.CRITICAL,
        description="AWS Access Key ID detected",
    ),
    PatternRule(
        name="AWS Secret Access Key",
        pattern=r"(?i)aws[_\-]?secret[_\-]?access[_\-]?key['\"]?\s*[:=]\s*['\"]?([A-Za-z0-9/+=]{40})",
        finding_type=FindingType.API_KEY,
        severity=FindingSeverity.CRITICAL,
        description="AWS Secret Access Key detected",
    ),
    PatternRule(
        name="GitHub Personal Access Token",
        pattern=r"ghp_[a-zA-Z0-9]{36}",
        finding_type=FindingType.API_KEY,
        severity=FindingSeverity.CRITICAL,
        description="GitHub Personal Access Token detected",
    ),
    PatternRule(
        name="GitHub OAuth Token",
        pattern=r"gho_[a-zA-Z0-9]{36}",
        finding_type=FindingType.API_KEY,
        severity=FindingSeverity.CRITICAL,
        description="GitHub OAuth Token detected",
    ),
    PatternRule(
        name="GitHub App Token",
        pattern=r"ghu_[a-zA-Z0-9]{36}",
        finding_type=FindingType.API_KEY,
        severity=FindingSeverity.CRITICAL,
        description="GitHub App Token detected",
    ),
    PatternRule(
        name="GitHub Fine-grained Token",
        pattern=r"github_pat_[a-zA-Z0-9]{22}_[a-zA-Z0-9]{59}",
        finding_type=FindingType.API_KEY,
        severity=FindingSeverity.CRITICAL,
        description="GitHub Fine-grained Personal Access Token detected",
    ),
    PatternRule(
        name="Stripe Live Key",
        pattern=r"sk_live_[a-zA-Z0-9]{24,}",
        finding_type=FindingType.API_KEY,
        severity=FindingSeverity.CRITICAL,
        description="Stripe Live Secret Key detected",
    ),
    PatternRule(
        name="Stripe Test Key",
        pattern=r"sk_test_[a-zA-Z0-9]{24,}",
        finding_type=FindingType.API_KEY,
        severity=FindingSeverity.MEDIUM,
        description="Stripe Test Secret Key detected (test mode)",
    ),
    PatternRule(
        name="Stripe Publishable Key",
        pattern=r"pk_(live|test)_[a-zA-Z0-9]{24,}",
        finding_type=FindingType.API_KEY,
        severity=FindingSeverity.LOW,
        description="Stripe Publishable Key detected (usually safe to share)",
    ),
    PatternRule(
        name="Google API Key",
        pattern=r"AIza[0-9A-Za-z\-_]{35}",
        finding_type=FindingType.API_KEY,
        severity=FindingSeverity.HIGH,
        description="Google API Key detected",
    ),
    PatternRule(
        name="Slack Token",
        pattern=r"xox[baprs]-[0-9a-zA-Z]{10,48}",
        finding_type=FindingType.API_KEY,
        severity=FindingSeverity.CRITICAL,
        description="Slack Token detected",
    ),
    PatternRule(
        name="Discord Token",
        pattern=r"[MN][A-Za-z\d]{23,}\.[\w-]{6}\.[\w-]{27}",
        finding_type=FindingType.API_KEY,
        severity=FindingSeverity.CRITICAL,
        description="Discord Bot Token detected",
    ),
    PatternRule(
        name="Twilio API Key",
        pattern=r"SK[a-fA-F0-9]{32}",
        finding_type=FindingType.API_KEY,
        severity=FindingSeverity.CRITICAL,
        description="Twilio API Key detected",
    ),
    PatternRule(
        name="SendGrid API Key",
        pattern=r"SG\.[a-zA-Z0-9\-_]{22}\.[a-zA-Z0-9\-_]{43}",
        finding_type=FindingType.API_KEY,
        severity=FindingSeverity.CRITICAL,
        description="SendGrid API Key detected",
    ),
    PatternRule(
        name="Mailchimp API Key",
        pattern=r"[a-f0-9]{32}-us[0-9]{1,2}",
        finding_type=FindingType.API_KEY,
        severity=FindingSeverity.CRITICAL,
        description="Mailchimp API Key detected",
    ),
    PatternRule(
        name="NPM Token",
        pattern=r"npm_[a-zA-Z0-9]{36}",
        finding_type=FindingType.API_KEY,
        severity=FindingSeverity.CRITICAL,
        description="NPM Access Token detected",
    ),
    PatternRule(
        name="PyPI Token",
        pattern=r"pypi-[a-zA-Z0-9]{32,}",
        finding_type=FindingType.API_KEY,
        severity=FindingSeverity.CRITICAL,
        description="PyPI API Token detected",
    ),
    PatternRule(
        name="Heroku API Key",
        pattern=r"[h|H]eroku[_\-]?[a|A][p|P][i|I][_\-]?[k|K]ey['\"]?\s*[:=]\s*['\"]?[a-f0-9\-]{36}",
        finding_type=FindingType.API_KEY,
        severity=FindingSeverity.CRITICAL,
        description="Heroku API Key detected",
    ),
    PatternRule(
        name="Firebase API Key",
        pattern=r"['\"]?firebase[_\-]?api[_\-]?key['\"]?\s*[:=]\s*['\"]?AIza[0-9A-Za-z\-_]{35}",
        finding_type=FindingType.API_KEY,
        severity=FindingSeverity.HIGH,
        description="Firebase API Key detected",
    ),

    # -------------------------------------------------------------------------
    # GENERIC API KEY PATTERNS
    # -------------------------------------------------------------------------
    PatternRule(
        name="Generic API Key Assignment",
        pattern=r"(?i)(api[_\-]?key|apikey|api[_\-]?secret)['\"]?\s*[:=]\s*['\"]?([a-zA-Z0-9\-_]{20,})['\"]?",
        finding_type=FindingType.API_KEY,
        severity=FindingSeverity.HIGH,
        description="Generic API key assignment detected",
    ),
    PatternRule(
        name="Generic Secret Assignment",
        pattern=r"(?i)(secret|secret[_\-]?key)['\"]?\s*[:=]\s*['\"]?([a-zA-Z0-9\-_]{16,})['\"]?",
        finding_type=FindingType.API_KEY,
        severity=FindingSeverity.HIGH,
        description="Generic secret assignment detected",
    ),
    PatternRule(
        name="Bearer Token",
        pattern=r"(?i)bearer\s+[a-zA-Z0-9\-_\.]{20,}",
        finding_type=FindingType.TOKEN,
        severity=FindingSeverity.HIGH,
        description="Bearer token detected",
    ),
    PatternRule(
        name="Authorization Header",
        pattern=r"(?i)authorization['\"]?\s*[:=]\s*['\"]?(bearer|basic|token)\s+[a-zA-Z0-9\-_\.=]+",
        finding_type=FindingType.TOKEN,
        severity=FindingSeverity.HIGH,
        description="Authorization header with token detected",
    ),

    # -------------------------------------------------------------------------
    # PASSWORDS
    # -------------------------------------------------------------------------
    PatternRule(
        name="Password Assignment",
        pattern=r"(?i)(password|passwd|pwd)['\"]?\s*[:=]\s*['\"]?([^\s'\"]{6,})['\"]?",
        finding_type=FindingType.PASSWORD,
        severity=FindingSeverity.CRITICAL,
        description="Password assignment detected",
    ),
    PatternRule(
        name="Password in URL",
        pattern=r"(?i)://[^:]+:([^@]+)@",
        finding_type=FindingType.PASSWORD,
        severity=FindingSeverity.CRITICAL,
        description="Password in URL detected",
    ),

    # -------------------------------------------------------------------------
    # CONNECTION STRINGS
    # -------------------------------------------------------------------------
    PatternRule(
        name="Database Connection String",
        pattern=r"(?i)(postgres|mysql|mongodb|redis|amqp)://[^\s]+",
        finding_type=FindingType.CONNECTION_STRING,
        severity=FindingSeverity.CRITICAL,
        description="Database connection string detected",
    ),
    PatternRule(
        name="JDBC Connection String",
        pattern=r"jdbc:[a-z]+://[^\s]+",
        finding_type=FindingType.CONNECTION_STRING,
        severity=FindingSeverity.CRITICAL,
        description="JDBC connection string detected",
    ),

    # -------------------------------------------------------------------------
    # PRIVATE KEYS
    # -------------------------------------------------------------------------
    PatternRule(
        name="RSA Private Key",
        pattern=r"-----BEGIN RSA PRIVATE KEY-----",
        finding_type=FindingType.PRIVATE_KEY,
        severity=FindingSeverity.CRITICAL,
        description="RSA Private Key detected",
    ),
    PatternRule(
        name="OpenSSH Private Key",
        pattern=r"-----BEGIN OPENSSH PRIVATE KEY-----",
        finding_type=FindingType.PRIVATE_KEY,
        severity=FindingSeverity.CRITICAL,
        description="OpenSSH Private Key detected",
    ),
    PatternRule(
        name="EC Private Key",
        pattern=r"-----BEGIN EC PRIVATE KEY-----",
        finding_type=FindingType.PRIVATE_KEY,
        severity=FindingSeverity.CRITICAL,
        description="EC Private Key detected",
    ),
    PatternRule(
        name="PGP Private Key",
        pattern=r"-----BEGIN PGP PRIVATE KEY BLOCK-----",
        finding_type=FindingType.PRIVATE_KEY,
        severity=FindingSeverity.CRITICAL,
        description="PGP Private Key detected",
    ),

    # -------------------------------------------------------------------------
    # PII - Personal Identifiable Information
    # -------------------------------------------------------------------------
    PatternRule(
        name="Email Address",
        pattern=r"[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}",
        finding_type=FindingType.PII_EMAIL,
        severity=FindingSeverity.MEDIUM,
        description="Email address detected",
    ),
    PatternRule(
        name="US Phone Number",
        pattern=r"(?:\+1[-.\s]?)?\(?[0-9]{3}\)?[-.\s]?[0-9]{3}[-.\s]?[0-9]{4}",
        finding_type=FindingType.PII_PHONE,
        severity=FindingSeverity.MEDIUM,
        description="US phone number detected",
    ),
    PatternRule(
        name="US Social Security Number",
        pattern=r"\b[0-9]{3}[-\s]?[0-9]{2}[-\s]?[0-9]{4}\b",
        finding_type=FindingType.PII_SSN,
        severity=FindingSeverity.CRITICAL,
        description="Possible US SSN detected",
    ),
    PatternRule(
        name="Credit Card Number",
        # Matches Visa, MasterCard, Amex, Discover with optional dashes/spaces
        pattern=r"\b(?:4[0-9]{3}[-\s]?[0-9]{4}[-\s]?[0-9]{4}[-\s]?[0-9]{4}|4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{2}[-\s]?[0-9]{4}[-\s]?[0-9]{4}[-\s]?[0-9]{4}|5[1-5][0-9]{14}|3[47][0-9]{2}[-\s]?[0-9]{6}[-\s]?[0-9]{5}|3[47][0-9]{13}|6(?:011|5[0-9]{2})[-\s]?[0-9]{4}[-\s]?[0-9]{4}[-\s]?[0-9]{4}|6(?:011|5[0-9]{2})[0-9]{12})\b",
        finding_type=FindingType.PII_CREDIT_CARD,
        severity=FindingSeverity.CRITICAL,
        description="Credit card number detected",
    ),

    # -------------------------------------------------------------------------
    # FILE PATHS
    # -------------------------------------------------------------------------
    PatternRule(
        name="Windows User Path",
        pattern=r"C:\\Users\\[^\\]+",
        finding_type=FindingType.FILE_PATH,
        severity=FindingSeverity.LOW,
        description="Windows user path with username detected",
    ),
    PatternRule(
        name="Unix Home Path",
        pattern=r"/(?:home|Users)/[^/\s]+",
        finding_type=FindingType.FILE_PATH,
        severity=FindingSeverity.LOW,
        description="Unix home path with username detected",
    ),
    PatternRule(
        name="Tilde Home Path",
        pattern=r"~/[^\s]+",
        finding_type=FindingType.FILE_PATH,
        severity=FindingSeverity.INFO,
        description="Home directory path detected",
    ),
]


# Custom patterns storage
_custom_patterns: List[PatternRule] = []


def add_custom_pattern(
    name: str,
    pattern: str,
    finding_type: FindingType = FindingType.CUSTOM,
    severity: FindingSeverity = FindingSeverity.HIGH,
    description: str = "Custom pattern match",
) -> PatternRule:
    """Add a custom pattern rule."""
    rule = PatternRule(
        name=name,
        pattern=pattern,
        finding_type=finding_type,
        severity=severity,
        description=description,
    )
    _custom_patterns.append(rule)
    return rule


def load_custom_patterns(config_path: Optional[Path] = None) -> List[PatternRule]:
    """
    Load custom patterns from config file.

    Config format (JSON):
    {
        "patterns": [
            {
                "name": "Company Internal ID",
                "pattern": "ACME-[0-9]{8}",
                "severity": "high",
                "description": "Internal ACME ID detected"
            }
        ]
    }
    """
    if config_path is None:
        config_path = Path.home() / ".sparknet" / "security_patterns.json"

    if not config_path.exists():
        return []

    try:
        with open(config_path) as f:
            config = json.load(f)

        patterns = []
        for p in config.get("patterns", []):
            severity = FindingSeverity(p.get("severity", "high").lower())
            finding_type = FindingType(p.get("type", "custom").lower())

            rule = PatternRule(
                name=p["name"],
                pattern=p["pattern"],
                finding_type=finding_type,
                severity=severity,
                description=p.get("description", "Custom pattern match"),
            )
            patterns.append(rule)
            _custom_patterns.append(rule)

        return patterns
    except Exception as e:
        print(f"Warning: Failed to load custom patterns: {e}")
        return []


def get_all_patterns() -> List[PatternRule]:
    """Get all patterns (built-in + custom)."""
    return BUILTIN_PATTERNS + _custom_patterns
