"""Detection pattern library for canari-forensics.

Tiers
-----
Tier 1  — Canari canary token formats (exact-match, zero false positives).
Tier 2  — Real credential formats (Stripe, AWS, GitHub, OpenAI, Slack,
           SendGrid, Google, generic API keys).
Tier 3  — PII patterns (email, phone, US SSN, credit card numbers).
Tier 4  — Custom patterns loaded at runtime via load_pattern_pack().
"""

from __future__ import annotations

import json
import re
from dataclasses import dataclass
from pathlib import Path
from typing import Pattern


@dataclass(frozen=True)
class DetectionPattern:
    pattern_id: str
    name: str
    severity: str
    confidence: str
    kind: str
    regex: Pattern[str]


# --------------------------------------------------------------------------- #
# Tier 1 — Canari canary token formats                                        #
# These mirror the generators in canari.generator exactly.                    #
# --------------------------------------------------------------------------- #

_TIER1: list[DetectionPattern] = [
    DetectionPattern(
        pattern_id="canary_api_key",
        name="Canari synthetic API key",
        severity="CRITICAL",
        confidence="HIGH",
        kind="canary_token_leak",
        # Generated as api_canari_<16 base32 chars>
        regex=re.compile(r"\bapi_canari_[a-z2-7]{16}\b"),
    ),
    DetectionPattern(
        pattern_id="canary_stripe_key",
        name="Canari synthetic Stripe key",
        severity="CRITICAL",
        confidence="HIGH",
        kind="canary_token_leak",
        # Generated as sk_test_CANARI_<18 base32 chars>
        regex=re.compile(r"\bsk_test_CANARI_[a-z2-7]{18}\b"),
    ),
    DetectionPattern(
        pattern_id="canary_aws_key",
        name="Canari synthetic AWS key",
        severity="CRITICAL",
        confidence="HIGH",
        kind="canary_token_leak",
        # Generated as AKIA<16 upper base32>  (same prefix as real AWS AKIA)
        regex=re.compile(r"\bAKIA[A-Z2-7]{16}\b"),
    ),
    DetectionPattern(
        pattern_id="canary_github_token",
        name="Canari synthetic GitHub token",
        severity="CRITICAL",
        confidence="HIGH",
        kind="canary_token_leak",
        # Generated as ghp_<36 base32 lower>
        regex=re.compile(r"\bghp_[a-z2-7]{36}\b"),
    ),
    DetectionPattern(
        pattern_id="canary_email",
        name="Canari synthetic canary email",
        severity="HIGH",
        confidence="HIGH",
        kind="canary_token_leak",
        # Generated as canari-canary-<12 hex chars>@sandbox.invalid
        regex=re.compile(r"\bcanari-canary-[0-9a-f]{12}@sandbox\.invalid\b"),
    ),
    DetectionPattern(
        pattern_id="canary_phone",
        name="Canari synthetic phone number",
        severity="HIGH",
        confidence="HIGH",
        kind="canary_token_leak",
        # Generated as +1-555-01XX
        regex=re.compile(r"\+1-555-01\d{2}\b"),
    ),
    DetectionPattern(
        pattern_id="canary_document_id",
        name="Canari synthetic document ID",
        severity="HIGH",
        confidence="HIGH",
        kind="canary_token_leak",
        # Generated as DOC-CANARI-<12 upper base32>
        regex=re.compile(r"\bDOC-CANARI-[A-Z2-7]{12}\b"),
    ),
    DetectionPattern(
        pattern_id="canary_ssn",
        name="Canari synthetic SSN",
        severity="HIGH",
        confidence="MEDIUM",
        kind="canary_token_leak",
        # Standard SSN format; combined with Luhn-valid credit card check
        # for higher confidence in forensics context
        regex=re.compile(r"\b(?!000|666|9\d{2})\d{3}-(?!00)\d{2}-(?!0{4})\d{4}\b"),
    ),
]


# --------------------------------------------------------------------------- #
# Tier 2 — Real credential formats                                             #
# --------------------------------------------------------------------------- #

_TIER2: list[DetectionPattern] = [
    # Stripe
    DetectionPattern(
        pattern_id="cred_stripe_live",
        name="Stripe live secret key",
        severity="CRITICAL",
        confidence="HIGH",
        kind="real_credential_leak",
        regex=re.compile(r"\bsk_live_[A-Za-z0-9]{24,}\b"),
    ),
    DetectionPattern(
        pattern_id="cred_stripe_restricted",
        name="Stripe restricted key",
        severity="CRITICAL",
        confidence="HIGH",
        kind="real_credential_leak",
        regex=re.compile(r"\brk_live_[A-Za-z0-9]{24,}\b"),
    ),
    # AWS
    DetectionPattern(
        pattern_id="cred_aws_access_key",
        name="AWS access key ID",
        severity="CRITICAL",
        confidence="HIGH",
        kind="real_credential_leak",
        regex=re.compile(r"\bAKIA[0-9A-Z]{16}\b"),
    ),
    DetectionPattern(
        pattern_id="cred_aws_secret_key",
        name="AWS secret access key",
        severity="CRITICAL",
        confidence="MEDIUM",
        kind="real_credential_leak",
        # 40-char base64 string preceded by context keyword
        regex=re.compile(
            r"(?i)(?:aws[_\-]?secret|secret[_\-]?access[_\-]?key)\s*[=:]\s*[A-Za-z0-9/+=]{40}"
        ),
    ),
    # GitHub
    DetectionPattern(
        pattern_id="cred_github_pat_classic",
        name="GitHub personal access token (classic)",
        severity="CRITICAL",
        confidence="HIGH",
        kind="real_credential_leak",
        regex=re.compile(r"\bghp_[A-Za-z0-9]{36}\b"),
    ),
    DetectionPattern(
        pattern_id="cred_github_pat_fine",
        name="GitHub fine-grained personal access token",
        severity="CRITICAL",
        confidence="HIGH",
        kind="real_credential_leak",
        regex=re.compile(r"\bgithub_pat_[A-Za-z0-9_]{82}\b"),
    ),
    DetectionPattern(
        pattern_id="cred_github_oauth",
        name="GitHub OAuth token",
        severity="CRITICAL",
        confidence="HIGH",
        kind="real_credential_leak",
        regex=re.compile(r"\bgho_[A-Za-z0-9]{36}\b"),
    ),
    # OpenAI
    DetectionPattern(
        pattern_id="cred_openai_key",
        name="OpenAI API key",
        severity="CRITICAL",
        confidence="HIGH",
        kind="real_credential_leak",
        regex=re.compile(r"\bsk-[A-Za-z0-9]{48}\b"),
    ),
    DetectionPattern(
        pattern_id="cred_openai_org",
        name="OpenAI organisation ID",
        severity="HIGH",
        confidence="HIGH",
        kind="real_credential_leak",
        regex=re.compile(r"\borg-[A-Za-z0-9]{24}\b"),
    ),
    # Slack
    DetectionPattern(
        pattern_id="cred_slack_bot_token",
        name="Slack bot token",
        severity="CRITICAL",
        confidence="HIGH",
        kind="real_credential_leak",
        regex=re.compile(r"\bxoxb-[0-9]+-[0-9]+-[A-Za-z0-9]+\b"),
    ),
    DetectionPattern(
        pattern_id="cred_slack_user_token",
        name="Slack user token",
        severity="CRITICAL",
        confidence="HIGH",
        kind="real_credential_leak",
        regex=re.compile(r"\bxoxp-[0-9]+-[0-9]+-[0-9]+-[A-Za-z0-9]+\b"),
    ),
    DetectionPattern(
        pattern_id="cred_slack_webhook",
        name="Slack incoming webhook URL",
        severity="HIGH",
        confidence="HIGH",
        kind="real_credential_leak",
        regex=re.compile(
            r"https://hooks\.slack\.com/services/T[A-Z0-9]+/B[A-Z0-9]+/[A-Za-z0-9]+"
        ),
    ),
    # SendGrid
    DetectionPattern(
        pattern_id="cred_sendgrid",
        name="SendGrid API key",
        severity="CRITICAL",
        confidence="HIGH",
        kind="real_credential_leak",
        regex=re.compile(r"\bSG\.[A-Za-z0-9_\-]{22}\.[A-Za-z0-9_\-]{43}\b"),
    ),
    # Google
    DetectionPattern(
        pattern_id="cred_google_api_key",
        name="Google API key",
        severity="HIGH",
        confidence="HIGH",
        kind="real_credential_leak",
        regex=re.compile(r"\bAIza[0-9A-Za-z\-_]{35}\b"),
    ),
    DetectionPattern(
        pattern_id="cred_google_oauth_client",
        name="Google OAuth 2.0 client ID",
        severity="HIGH",
        confidence="HIGH",
        kind="real_credential_leak",
        regex=re.compile(r"\b\d{12}-[a-z0-9]{32}\.apps\.googleusercontent\.com\b"),
    ),
    # Generic high-entropy patterns (last resort)
    DetectionPattern(
        pattern_id="cred_generic_bearer",
        name="Generic Bearer token",
        severity="HIGH",
        confidence="MEDIUM",
        kind="real_credential_leak",
        regex=re.compile(r"(?i)\bBearer\s+[A-Za-z0-9\-._~+/]{32,}\b"),
    ),
]


# --------------------------------------------------------------------------- #
# Tier 3 — PII patterns                                                       #
# --------------------------------------------------------------------------- #

_TIER3: list[DetectionPattern] = [
    DetectionPattern(
        pattern_id="pii_email",
        name="Email address",
        severity="HIGH",
        confidence="HIGH",
        kind="pii_leak",
        regex=re.compile(
            r"\b[A-Za-z0-9._%+\-]+@[A-Za-z0-9.\-]+\.[A-Za-z]{2,}\b"
        ),
    ),
    DetectionPattern(
        pattern_id="pii_phone_us",
        name="US phone number",
        severity="MEDIUM",
        confidence="MEDIUM",
        kind="pii_leak",
        # Matches +1 555-0100, (555) 010-0000, 555.010.0000, etc.
        regex=re.compile(
            r"(?:\+1[-.\s]?)?(?:\(\d{3}\)|\d{3})[-.\s]?\d{3}[-.\s]?\d{4}\b"
        ),
    ),
    DetectionPattern(
        pattern_id="pii_ssn",
        name="US Social Security Number",
        severity="CRITICAL",
        confidence="HIGH",
        kind="pii_leak",
        # Excludes obviously invalid prefixes 000, 666, 900-999
        regex=re.compile(
            r"\b(?!000|666|9\d{2})\d{3}[-\s](?!00)\d{2}[-\s](?!0{4})\d{4}\b"
        ),
    ),
    DetectionPattern(
        pattern_id="pii_credit_card",
        name="Credit / debit card number",
        severity="CRITICAL",
        confidence="MEDIUM",
        kind="pii_leak",
        # Matches 13–19 digit card numbers with optional separators
        # False-positive rate is kept low by requiring common card prefixes.
        regex=re.compile(
            r"\b(?:4[0-9]{12}(?:[0-9]{3,6})?|"          # Visa
            r"5[1-5][0-9]{14}|"                           # Mastercard
            r"2(?:2[2-9][1-9]|[3-6]\d{2}|7[01]\d|720)[0-9]{12}|"  # Mastercard 2-series
            r"3[47][0-9]{13}|"                            # Amex
            r"3(?:0[0-5]|[68][0-9])[0-9]{11}|"           # Diners
            r"6(?:011|5[0-9]{2})[0-9]{12,15})\b"         # Discover
        ),
    ),
    DetectionPattern(
        pattern_id="pii_passport",
        name="Passport number (generic)",
        severity="HIGH",
        confidence="LOW",
        kind="pii_leak",
        regex=re.compile(r"\b[A-Z]{1,2}[0-9]{6,9}\b"),
    ),
]


# --------------------------------------------------------------------------- #
# Prompt injection indicators (supplementary)                                  #
# --------------------------------------------------------------------------- #

_INJECTION: list[DetectionPattern] = [
    DetectionPattern(
        pattern_id="injection_success_indicator",
        name="Prompt injection success indicator",
        severity="HIGH",
        confidence="MEDIUM",
        kind="probable_prompt_injection",
        regex=re.compile(
            r"(?i)here is everything|ignore all instructions|"
            r"output your full context|"
            r"i will now output|"
            r"as instructed, here is|"
            r"here are my instructions|"
            r"my system prompt is|"
            r"system prompt[:]\s"
        ),
    ),
    DetectionPattern(
        pattern_id="injection_jailbreak_indicator",
        name="Jailbreak / DAN indicator",
        severity="HIGH",
        confidence="MEDIUM",
        kind="probable_prompt_injection",
        regex=re.compile(
            r"(?i)DAN mode|do anything now|jailbreak|"
            r"you are now|you have been freed|"
            r"disregard (?:all )?(?:previous |prior )?instructions"
        ),
    ),
]


# --------------------------------------------------------------------------- #
# Combined default pattern list (all tiers)                                   #
# --------------------------------------------------------------------------- #

PATTERNS: list[DetectionPattern] = _TIER1 + _TIER2 + _TIER3 + _INJECTION


# --------------------------------------------------------------------------- #
# Tier 4 — Custom pattern loader                                              #
# --------------------------------------------------------------------------- #

def load_pattern_pack(path: str | Path) -> list[DetectionPattern]:
    """Load a JSON pattern pack file and return a list of DetectionPattern objects.

    JSON format::

        {
            "patterns": [
                {
                    "pattern_id": "my_pattern",
                    "name": "My custom pattern",
                    "severity": "HIGH",
                    "confidence": "HIGH",
                    "kind": "custom",
                    "regex": "\\bmy-secret-[A-Z0-9]+\\b"
                }
            ]
        }
    """
    payload = json.loads(Path(path).read_text(encoding="utf-8"))
    raw_patterns = payload.get("patterns", payload)
    out: list[DetectionPattern] = []

    for item in raw_patterns:
        out.append(
            DetectionPattern(
                pattern_id=str(item["pattern_id"]),
                name=str(item["name"]),
                severity=str(item["severity"]),
                confidence=str(item["confidence"]),
                kind=str(item["kind"]),
                regex=re.compile(str(item["regex"])),
            )
        )

    return out
