"""Contradiction Detection Engine — NLI-based belief revision.

Detects conflicting facts in the knowledge base using Natural Language Inference.
Supports both keyword-based (fast) and DeBERTa NLI model (accurate) modes.
When contradictions are found, the older fact is deprecated and the newer one takes over.
"""

from __future__ import annotations

import re
from dataclasses import dataclass, field
from datetime import datetime, timezone
from typing import Optional

from mnemosynth.core.types import MemoryNode, MemoryStatus


@dataclass
class Contradiction:
    """A detected contradiction between two memories."""
    memory_a: MemoryNode
    memory_b: MemoryNode
    score: float  # 0.0 to 1.0 — how contradictory
    explanation: str = ""
    resolved: bool = False


@dataclass
class ContradictionReport:
    """Report from a contradiction scan."""
    total_checked: int = 0
    contradictions_found: int = 0
    contradictions_resolved: int = 0
    items: list[Contradiction] = field(default_factory=list)


# Antonym pairs for keyword-based detection
ANTONYM_PAIRS = [
    ("like", "hate"), ("love", "hate"), ("prefer", "avoid"),
    ("always", "never"), ("enable", "disable"), ("open", "close"),
    ("start", "stop"), ("true", "false"), ("yes", "no"),
    ("fast", "slow"), ("good", "bad"), ("right", "wrong"),
    ("light", "dark"), ("frontend", "backend"), ("python", "java"),
    ("react", "angular"), ("tabs", "spaces"), ("vim", "emacs"),
    ("typescript", "javascript"), ("mac", "windows"), ("linux", "windows"),
]


class ContradictionEngine:
    """Detects and resolves contradictory memories.

    Two modes:
    - Keyword-based (default): Fast antonym pair detection
    - NLI model: Uses DeBERTa-v3-small for accurate entailment scoring
    """

    def __init__(self, config=None, use_model: bool = False):
        self.config = config
        self.use_model = use_model
        self._model = None
        self.threshold = config.contradiction.threshold if config else 0.85

    def check_pair(self, memory_a: MemoryNode, memory_b: MemoryNode) -> Optional[Contradiction]:
        """Check if two memories contradict each other.

        Returns a Contradiction if detected, None otherwise.
        """
        if memory_a.id == memory_b.id:
            return None

        if self.use_model:
            score = self._nli_score(memory_a.content, memory_b.content)
        else:
            score = self._keyword_score(memory_a.content, memory_b.content)

        if score >= self.threshold:
            return Contradiction(
                memory_a=memory_a,
                memory_b=memory_b,
                score=score,
                explanation=self._explain(memory_a.content, memory_b.content),
            )
        return None

    def scan(self, memories: list[MemoryNode]) -> ContradictionReport:
        """Scan a list of memories for contradictions.

        Only checks active semantic memories for efficiency.
        """
        report = ContradictionReport()
        active = [m for m in memories if m.status == MemoryStatus.ACTIVE]

        for i in range(len(active)):
            for j in range(i + 1, len(active)):
                report.total_checked += 1
                contradiction = self.check_pair(active[i], active[j])
                if contradiction:
                    report.contradictions_found += 1
                    report.items.append(contradiction)

        return report

    def resolve(self, contradiction: Contradiction) -> MemoryNode:
        """Resolve a contradiction by deprecating the older memory.

        Returns the winning (kept) memory.
        """
        # Keep the newer memory
        if contradiction.memory_a.created_at > contradiction.memory_b.created_at:
            winner = contradiction.memory_a
            loser = contradiction.memory_b
        else:
            winner = contradiction.memory_b
            loser = contradiction.memory_a

        # Deprecate the loser
        loser.deprecate(superseded_by_id=winner.id)
        winner.supersedes = loser.id

        contradiction.resolved = True
        return winner

    def _keyword_score(self, text_a: str, text_b: str) -> float:
        """Fast keyword-based contradiction scoring using antonym pairs."""
        words_a = set(re.findall(r'\b\w+\b', text_a.lower()))
        words_b = set(re.findall(r'\b\w+\b', text_b.lower()))

        # Check for shared context (same topic)
        shared = words_a & words_b
        if len(shared) < 2:
            return 0.0  # Not enough shared context to be contradictory

        # Check for antonym pairs
        contradiction_score = 0.0
        for pos, neg in ANTONYM_PAIRS:
            if (pos in words_a and neg in words_b) or (neg in words_a and pos in words_b):
                contradiction_score += 0.4

        # Check for negation patterns
        negation_words = {"not", "no", "never", "don't", "doesn't", "isn't", "aren't", "won't"}
        neg_a = bool(words_a & negation_words)
        neg_b = bool(words_b & negation_words)
        if neg_a != neg_b and len(shared) >= 3:
            contradiction_score += 0.3

        # Similarity boost: if statements are very similar but have antonyms → strong contradiction
        similarity = len(shared) / max(len(words_a | words_b), 1)
        if similarity > 0.5 and contradiction_score > 0:
            contradiction_score *= 1.5

        return min(1.0, contradiction_score)

    def _nli_score(self, text_a: str, text_b: str) -> float:
        """Score contradiction using NLI model (DeBERTa-v3-small)."""
        if self._model is None:
            try:
                from transformers import pipeline
                model_name = "cross-encoder/nli-deberta-v3-small"
                if self.config:
                    model_name = self.config.contradiction.model
                self._model = pipeline(
                    "text-classification",
                    model=model_name,
                    truncation=True,
                )
            except ImportError:
                return self._keyword_score(text_a, text_b)

        # NLI format: premise + hypothesis
        result = self._model(f"{text_a} [SEP] {text_b}")
        for item in result:
            if item["label"] == "CONTRADICTION":
                return item["score"]
        return 0.0

    def _explain(self, text_a: str, text_b: str) -> str:
        """Generate a human-readable explanation of the contradiction."""
        words_a = set(re.findall(r'\b\w+\b', text_a.lower()))
        words_b = set(re.findall(r'\b\w+\b', text_b.lower()))

        conflicts = []
        for pos, neg in ANTONYM_PAIRS:
            if (pos in words_a and neg in words_b):
                conflicts.append(f"'{pos}' vs '{neg}'")
            elif (neg in words_a and pos in words_b):
                conflicts.append(f"'{neg}' vs '{pos}'")

        if conflicts:
            return f"Conflicting terms: {', '.join(conflicts)}"
        return "Potential semantic contradiction detected"
