"""Guardian: blacklist management, threat detection, and content scanning.

Protects the user by maintaining a blacklist of harmful accounts, domains,
and patterns. Can be populated by user action or agent recommendation.
"""

from __future__ import annotations

import contextlib
import logging
import re
from dataclasses import dataclass
from datetime import UTC, datetime
from enum import StrEnum

from agntspace.vault.reader import VaultReader
from agntspace.vault.writer import VaultWriter

log = logging.getLogger(__name__)


class BlockCategory(StrEnum):
    """Categories of blacklisted items."""

    DOMAIN = "domain"          # Malicious/phishing websites
    EMAIL = "email"            # Spam or scam email addresses
    ACCOUNT = "account"        # Harmful social/messaging accounts
    PHONE = "phone"            # Spam phone numbers
    IP = "ip"                  # Malicious IPs
    KEYWORD = "keyword"        # Harmful content patterns
    URL_PATTERN = "url_pattern"  # URL patterns (regex)


@dataclass
class BlacklistEntry:
    """A single blacklist entry."""

    value: str
    category: str
    reason: str
    source: str  # "user" | "agent" | "system"
    added: str
    status: str = "active"  # "active" | "pending" (agent recommendation awaiting approval)


@dataclass
class ScanResult:
    """Result of scanning content against the blacklist."""

    safe: bool
    threats: list[dict]  # list of {value, category, reason}


class GuardianService:
    """Manages the blacklist and scans content for threats."""

    BLACKLIST_PATH = "system/security/blacklist.md"

    def __init__(self, vault_reader: VaultReader, vault_writer: VaultWriter) -> None:
        self._reader = vault_reader
        self._writer = vault_writer

    def get_blacklist(self) -> list[BlacklistEntry]:
        """Load and parse the blacklist from vault."""
        if not self._reader.exists(self.BLACKLIST_PATH):
            return []

        doc = self._reader.read(self.BLACKLIST_PATH)
        return self._parse_blacklist(doc.content)

    def add_entry(
        self,
        value: str,
        category: str,
        reason: str,
        source: str = "user",
        status: str = "active",
    ) -> BlacklistEntry:
        """Add an entry to the blacklist."""
        entry = BlacklistEntry(
            value=value,
            category=category,
            reason=reason,
            source=source,
            added=datetime.now(UTC).strftime("%Y-%m-%d"),
            status=status,
        )

        entries = self.get_blacklist()

        # Check for duplicates
        for existing in entries:
            if existing.value.lower() == value.lower() and existing.category == category:
                log.info("Duplicate blacklist entry, skipping: %s", value)
                return existing

        entries.append(entry)
        self._write_blacklist(entries)
        log.info("Blacklist entry added: %s (%s) by %s", value, category, source)
        return entry

    def remove_entry(self, value: str, category: str) -> bool:
        """Remove an entry from the blacklist. Returns True if found and removed."""
        entries = self.get_blacklist()
        original_len = len(entries)
        entries = [
            e for e in entries
            if not (e.value.lower() == value.lower() and e.category == category)
        ]
        if len(entries) < original_len:
            self._write_blacklist(entries)
            log.info("Blacklist entry removed: %s (%s)", value, category)
            return True
        return False

    def approve_recommendation(self, value: str, category: str) -> bool:
        """Approve a pending agent recommendation (change status to active)."""
        entries = self.get_blacklist()
        for entry in entries:
            if entry.value.lower() == value.lower() and entry.category == category:
                entry.status = "active"
                self._write_blacklist(entries)
                log.info("Blacklist recommendation approved: %s", value)
                return True
        return False

    def reject_recommendation(self, value: str, category: str) -> bool:
        """Reject and remove a pending agent recommendation."""
        return self.remove_entry(value, category)

    def scan_text(self, text: str) -> ScanResult:
        """Scan text content against the blacklist.

        Checks for:
        - Blacklisted domains/URLs in the text
        - Blacklisted email addresses
        - Blacklisted keywords/patterns
        - Blacklisted account references
        """
        entries = self.get_blacklist()
        active = [e for e in entries if e.status == "active"]
        threats: list[dict] = []

        lower_text = text.lower()

        for entry in active:
            matched = False

            if entry.category in (
                BlockCategory.DOMAIN,
                BlockCategory.EMAIL,
                BlockCategory.ACCOUNT,
                BlockCategory.KEYWORD,
            ):
                matched = entry.value.lower() in lower_text

            elif entry.category == BlockCategory.URL_PATTERN:
                with contextlib.suppress(re.error):
                    matched = bool(re.search(entry.value, text, re.IGNORECASE))

            elif entry.category == BlockCategory.PHONE:
                clean_phone = re.sub(r"[\s\-\(\)]", "", entry.value)
                clean_text = re.sub(r"[\s\-\(\)]", "", text)
                matched = clean_phone in clean_text

            elif entry.category == BlockCategory.IP and entry.value in text:
                matched = True

            if matched:
                threats.append({
                    "value": entry.value,
                    "category": entry.category,
                    "reason": entry.reason,
                })

        return ScanResult(safe=len(threats) == 0, threats=threats)

    def scan_url(self, url: str) -> ScanResult:
        """Scan a specific URL against domain and URL pattern blacklists."""
        return self.scan_text(url)

    def scan_sender(self, sender: str, channel: str = "") -> ScanResult:
        """Scan a message sender against account and email blacklists."""
        return self.scan_text(sender)

    def get_pending_recommendations(self) -> list[BlacklistEntry]:
        """Get agent recommendations awaiting user approval."""
        return [e for e in self.get_blacklist() if e.status == "pending"]

    def get_stats(self) -> dict:
        """Get blacklist statistics."""
        entries = self.get_blacklist()
        active = [e for e in entries if e.status == "active"]
        pending = [e for e in entries if e.status == "pending"]

        by_category: dict[str, int] = {}
        for e in active:
            by_category[e.category] = by_category.get(e.category, 0) + 1

        return {
            "total": len(entries),
            "active": len(active),
            "pending": len(pending),
            "by_category": by_category,
        }

    def _parse_blacklist(self, content: str) -> list[BlacklistEntry]:
        """Parse blacklist markdown table into entries."""
        entries = []
        for line in content.split("\n"):
            line = line.strip()
            if not line.startswith("|") or "---" in line or "Value" in line:
                continue
            cells = [c.strip() for c in line.split("|")[1:-1]]
            if len(cells) >= 5:
                entries.append(BlacklistEntry(
                    value=cells[0],
                    category=cells[1],
                    reason=cells[2],
                    source=cells[3],
                    added=cells[4],
                    status=cells[5] if len(cells) > 5 else "active",
                ))
        return entries

    def _write_blacklist(self, entries: list[BlacklistEntry]) -> None:
        """Write blacklist entries as a markdown table."""
        now = datetime.now(UTC).strftime("%Y-%m-%d")
        lines = [
            "---",
            'title: "Blacklist"',
            'description: "Blocked accounts, domains, and patterns"',
            f"updated: {now}",
            "---",
            "",
            "# Blacklist",
            "",
            "> Managed by Guardian. User-added and agent-recommended entries.",
            "> Pending entries need your approval before they take effect.",
            "",
            "| Value | Category | Reason | Source | Added | Status |",
            "|-------|----------|--------|--------|-------|--------|",
        ]

        for e in entries:
            lines.append(
                f"| {e.value} | {e.category} | {e.reason} "
                f"| {e.source} | {e.added} | {e.status} |"
            )

        lines.extend([
            "",
            "## Categories",
            "",
            "- **domain** — Malicious or phishing websites",
            "- **email** — Spam or scam email addresses",
            "- **account** — Harmful social/messaging accounts",
            "- **phone** — Spam phone numbers",
            "- **ip** — Malicious IP addresses",
            "- **keyword** — Harmful content patterns",
            "- **url_pattern** — URL patterns (regex)",
        ])

        self._writer.write(self.BLACKLIST_PATH, "\n".join(lines))
