"""
Artifact Extractor - Extract intelligence artifacts from content

Extracts emails, crypto addresses, domains, IPs, phone numbers, and other
indicators of compromise from scraped content.
"""

import re
from dataclasses import dataclass, field
from datetime import datetime


@dataclass
class Artifact:
    """Represents an extracted intelligence artifact."""

    artifact_type: str
    value: str
    context: str = ""
    source_url: str = ""
    confidence: float = 1.0
    extracted_at: datetime = field(default_factory=datetime.utcnow)

    def to_dict(self) -> dict:
        """Convert to dictionary."""
        return {
            "type": self.artifact_type,
            "value": self.value,
            "context": self.context,
            "source_url": self.source_url,
            "confidence": self.confidence,
            "extracted_at": self.extracted_at.isoformat(),
        }


class ArtifactExtractor:
    """
    Extract intelligence artifacts from content.

    Supports extraction of:
    - Email addresses
    - Bitcoin addresses (legacy and Bech32)
    - Ethereum addresses
    - .onion domains
    - IP addresses (IPv4)
    - Phone numbers
    - Usernames (common patterns)

    Usage:
        extractor = ArtifactExtractor()
        artifacts = extractor.extract_all(content, source_url="http://example.onion")
    """

    # Regex patterns for different artifact types
    PATTERNS = {
        "email": re.compile(
            r"[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}",
            re.IGNORECASE,
        ),
        "btc_legacy": re.compile(
            r"\b[13][a-km-zA-HJ-NP-Z1-9]{25,34}\b",
        ),
        "btc_bech32": re.compile(
            r"\bbc1[a-zA-HJ-NP-Z0-9]{39,59}\b",
        ),
        "eth": re.compile(
            r"\b0x[a-fA-F0-9]{40}\b",
        ),
        "onion": re.compile(
            r"\b[a-z2-7]{16,56}\.onion\b",
            re.IGNORECASE,
        ),
        "ip_v4": re.compile(
            r"\b(?:[0-9]{1,3}\.){3}[0-9]{1,3}\b",
        ),
        "phone": re.compile(
            r"\+?[1-9]\d{1,14}",
        ),
        "username": re.compile(
            r"(?:^|[\s@])@?([a-zA-Z0-9_]{3,30})(?:[\s,.]|$)",
        ),
        "xmr": re.compile(
            r"\b4[0-9AB][1-9A-HJ-NP-Za-km-z]{93}\b",
        ),
        "pgp_key": re.compile(
            r"-----BEGIN PGP PUBLIC KEY BLOCK-----[\s\S]+?-----END PGP PUBLIC KEY BLOCK-----",
        ),
    }

    # Context extraction window (characters before/after match)
    CONTEXT_WINDOW = 50

    def __init__(self, context_window: int = 50):
        """
        Initialize artifact extractor.

        Args:
            context_window: Characters of context to capture around matches
        """
        self.context_window = context_window

    def extract_all(
        self,
        content: str,
        source_url: str = "",
    ) -> list[Artifact]:
        """
        Extract all artifact types from content.

        Args:
            content: Text content to scan
            source_url: Source URL for attribution

        Returns:
            List of Artifact objects found
        """
        artifacts: list[Artifact] = []

        for artifact_type in self.PATTERNS:
            type_artifacts = self.extract_by_type(content, artifact_type, source_url)
            artifacts.extend(type_artifacts)

        return artifacts

    def extract_by_type(
        self,
        content: str,
        artifact_type: str,
        source_url: str = "",
    ) -> list[Artifact]:
        """
        Extract specific artifact type from content.

        Args:
            content: Text content to scan
            artifact_type: Type of artifact to extract (e.g., 'email', 'btc_legacy')
            source_url: Source URL for attribution

        Returns:
            List of Artifact objects of the specified type

        Raises:
            ValueError: If artifact type is not recognized
        """
        if artifact_type not in self.PATTERNS:
            raise ValueError(
                f"Unknown artifact type: {artifact_type}. Available: {list(self.PATTERNS.keys())}"
            )

        pattern = self.PATTERNS[artifact_type]
        artifacts: list[Artifact] = []
        seen_values: set[str] = set()

        for match in pattern.finditer(content):
            value = match.group(0) if artifact_type != "username" else match.group(1)

            # Skip duplicates
            if value.lower() in seen_values:
                continue
            seen_values.add(value.lower())

            # Validate artifact
            if not self._validate_artifact(artifact_type, value):
                continue

            # Extract context
            context = self._extract_context(content, match.start(), match.end())

            # Calculate confidence
            confidence = self._calculate_confidence(artifact_type, value, context)

            # Normalize artifact type name
            normalized_type = self._normalize_type(artifact_type)

            artifacts.append(
                Artifact(
                    artifact_type=normalized_type,
                    value=value,
                    context=context,
                    source_url=source_url,
                    confidence=confidence,
                )
            )

        return artifacts

    def _extract_context(self, content: str, start: int, end: int) -> str:
        """Extract surrounding context for a match."""
        context_start = max(0, start - self.context_window)
        context_end = min(len(content), end + self.context_window)
        context = content[context_start:context_end]
        # Clean up context
        context = re.sub(r"\s+", " ", context).strip()
        if context_start > 0:
            context = "..." + context
        if context_end < len(content):
            context = context + "..."
        return context

    def _validate_artifact(self, artifact_type: str, value: str) -> bool:
        """
        Validate an extracted artifact.

        Args:
            artifact_type: Type of artifact
            value: Extracted value

        Returns:
            True if valid, False otherwise
        """
        if artifact_type == "ip_v4":
            # Validate IP address octets
            octets = value.split(".")
            return all(0 <= int(octet) <= 255 for octet in octets)

        if artifact_type == "phone":
            # Filter out numbers that are too short or too long
            digits = re.sub(r"\D", "", value)
            return 7 <= len(digits) <= 15

        if artifact_type == "email":
            # Basic email validation
            return "@" in value and "." in value.split("@")[1]

        if artifact_type == "username":
            # Filter out common false positives
            false_positives = {
                "http",
                "https",
                "www",
                "com",
                "org",
                "net",
                "the",
                "and",
                "for",
                "not",
                "you",
                "all",
            }
            return value.lower() not in false_positives

        return True

    def _calculate_confidence(
        self,
        artifact_type: str,
        value: str,
        context: str,
    ) -> float:
        """
        Calculate confidence score for an artifact.

        Args:
            artifact_type: Type of artifact
            value: Extracted value
            context: Surrounding context

        Returns:
            Confidence score between 0.0 and 1.0
        """
        confidence = 1.0

        # Reduce confidence for short values
        if len(value) < 10:
            confidence *= 0.9

        # Reduce confidence if no relevant context
        context_lower = context.lower()
        if artifact_type == "email":
            if any(kw in context_lower for kw in ["contact", "email", "mail"]):
                confidence = min(1.0, confidence * 1.1)
        elif artifact_type in ("btc_legacy", "btc_bech32"):
            if any(kw in context_lower for kw in ["bitcoin", "btc", "wallet", "address"]):
                confidence = min(1.0, confidence * 1.1)
        elif artifact_type == "eth":
            if any(kw in context_lower for kw in ["ethereum", "eth", "wallet"]):
                confidence = min(1.0, confidence * 1.1)

        return round(confidence, 2)

    def _normalize_type(self, artifact_type: str) -> str:
        """Normalize artifact type name for storage."""
        type_map = {
            "btc_legacy": "btc",
            "btc_bech32": "btc",
            "ip_v4": "ip",
        }
        return type_map.get(artifact_type, artifact_type)

    def get_supported_types(self) -> list[str]:
        """Get list of supported artifact types."""
        return list(self.PATTERNS.keys())

    def extract_summary(self, content: str, source_url: str = "") -> dict[str, int]:
        """
        Get summary count of artifacts by type.

        Args:
            content: Text content to scan
            source_url: Source URL for attribution

        Returns:
            Dictionary mapping artifact types to counts
        """
        artifacts = self.extract_all(content, source_url)
        summary: dict[str, int] = {}
        for artifact in artifacts:
            summary[artifact.artifact_type] = summary.get(artifact.artifact_type, 0) + 1
        return summary
