"""
AgentDrop Shield -- Data models for the scanning pipeline.

All result types, configuration, and enumerations used across
the multi-layer file security scanner.
"""

from __future__ import annotations

import enum
from dataclasses import dataclass, field
from typing import Any, Optional


class ThreatLevel(enum.IntEnum):
    """Threat severity classification.

    Ordered by severity so comparisons work naturally:
        ThreatLevel.LOW < ThreatLevel.HIGH  # True
    """

    CLEAN = 0
    LOW = 1
    MEDIUM = 2
    HIGH = 3
    CRITICAL = 4

    def __str__(self) -> str:
        return self.name


class Strictness(enum.Enum):
    """Scanner strictness levels controlling which findings are flagged."""

    PERMISSIVE = "permissive"   # Only flag HIGH / CRITICAL
    STANDARD = "standard"       # Flag MEDIUM+ (default)
    STRICT = "strict"           # Flag LOW+
    PARANOID = "paranoid"       # Flag everything including CLEAN anomalies


# Map strictness to the minimum ThreatLevel that triggers a non-CLEAN verdict.
STRICTNESS_THRESHOLDS: dict[Strictness, ThreatLevel] = {
    Strictness.PERMISSIVE: ThreatLevel.HIGH,
    Strictness.STANDARD: ThreatLevel.MEDIUM,
    Strictness.STRICT: ThreatLevel.LOW,
    Strictness.PARANOID: ThreatLevel.CLEAN,
}


@dataclass(frozen=True)
class ScanConfig:
    """Configuration for a Shield scan.

    All limits use safe defaults that work for typical agent-to-agent transfers.
    Override per-scan or globally via Shield(config=ScanConfig(...)).
    """

    # --- Strictness ---
    strictness: Strictness = Strictness.STANDARD

    # --- Layer 0: Resource limits ---
    max_file_size_bytes: int = 100 * 1024 * 1024        # 100 MB
    max_decompressed_bytes: int = 200 * 1024 * 1024      # 200 MB
    max_decompression_ratio: float = 100.0                # 100:1
    max_archive_depth: int = 3
    max_archive_entries: int = 1000
    max_scan_time_seconds: float = 30.0
    max_text_length_chars: int = 10 * 1024 * 1024        # 10M characters
    max_json_depth: int = 50
    max_xml_depth: int = 50

    # --- Shield mode ---
    shield_mode: str = "review"                            # "block" or "review"

    # --- Layer 4: Injection detection ---
    injection_confidence_threshold: float = 0.4           # Minimum confidence to report
    decode_base64_payloads: bool = True
    multilingual_detection: bool = True


@dataclass(frozen=True)
class InjectionFinding:
    """A single prompt injection detection result."""

    line: int
    """1-based line number in the extracted text where the pattern matched."""

    pattern_name: str
    """Machine-readable identifier for the detection rule that fired."""

    confidence: float
    """Confidence score from 0.0 (noise) to 1.0 (certain injection)."""

    matched_text: str
    """The exact text fragment that triggered the detection."""

    description: str
    """Human-readable explanation of the threat."""

    threat_level: ThreatLevel = ThreatLevel.MEDIUM
    """Severity of this individual finding."""


@dataclass
class ScanResult:
    """Composite result of the full Shield scanning pipeline.

    Returned by ``Shield.scan()`` -- contains the verdict, all findings,
    warnings, timing, and per-layer detail.
    """

    threat_level: ThreatLevel
    """Overall threat level -- the maximum across all layers."""

    malware_clean: bool
    """True if no known-malware signatures were detected (Layer 2 placeholder)."""

    injection_findings: list[InjectionFinding] = field(default_factory=list)
    """All prompt injection findings from Layer 4."""

    warnings: list[str] = field(default_factory=list)
    """Non-blocking warnings (e.g. suspicious metadata, minor anomalies)."""

    scan_time_ms: int = 0
    """Wall-clock scan duration in milliseconds."""

    intent_score: float = 0.0
    """Layer 4b intent classifier score: 0.0 (benign) to 1.0 (instructive)."""

    semantic_score: float = 0.0
    """Layer 4c semantic analyzer score: 0.0 (benign) to 1.0 (suspicious)."""

    sender_reputation: Optional[float] = None
    """Layer 5b sender reputation: 0.0 (distrusted) to 1.0 (trusted).
    None if no sender_id was provided or no behavioral tracker is active."""

    details: dict[str, Any] = field(default_factory=dict)
    """Per-layer detail dicts keyed by layer name."""

    needs_review: bool = False
    """True when shield_mode='review' and the file would have been blocked."""

    review_prompt: Optional[str] = None
    """Sanitized prompt for LLM evaluation. Contains NO raw file content."""

    safe: bool = True
    """True when the file is safe to process. False only in block mode for HIGH+."""

    @property
    def is_clean(self) -> bool:
        """True when the overall verdict is CLEAN."""
        return self.threat_level == ThreatLevel.CLEAN

    @property
    def blocked(self) -> bool:
        """True when the file should be quarantined (HIGH or CRITICAL)."""
        return self.threat_level >= ThreatLevel.HIGH

    def summary(self) -> str:
        """One-line human-readable summary."""
        parts = [f"threat={self.threat_level.name}"]
        if self.injection_findings:
            parts.append(f"injections={len(self.injection_findings)}")
        if self.warnings:
            parts.append(f"warnings={len(self.warnings)}")
        parts.append(f"time={self.scan_time_ms}ms")
        return " | ".join(parts)
