"""Fact Extractor — extracts structured facts from conversations.

Parses raw text to identify memory-worthy statements and classifies them.
Uses rule-based extraction (LLM-based extraction planned for v0.2).
"""

from __future__ import annotations

import re
from dataclasses import dataclass

from mnemosynth.core.types import MemoryType


@dataclass
class ExtractedFact:
    """A single fact extracted from text."""
    content: str
    memory_type: MemoryType
    confidence: float


class FactExtractor:
    """Extract structured facts from conversation text.

    Currently uses rule-based extraction.
    LLM-based extraction will be added in v0.2 for better accuracy.
    """

    def extract(self, text: str) -> list[ExtractedFact]:
        """Extract facts from a block of text.

        Returns a list of ExtractedFact objects.
        """
        facts: list[ExtractedFact] = []

        # Split into sentences
        sentences = self._split_sentences(text)

        for sentence in sentences:
            sentence = sentence.strip()
            if len(sentence) < 10:  # Skip very short fragments
                continue

            # Classify each sentence
            fact_type = self._classify_sentence(sentence)
            confidence = self._estimate_confidence(sentence)

            facts.append(ExtractedFact(
                content=sentence,
                memory_type=fact_type,
                confidence=confidence,
            ))

        return facts

    def extract_from_conversation(
        self,
        user_message: str,
        assistant_response: str,
    ) -> list[ExtractedFact]:
        """Extract facts from a user-assistant exchange."""
        facts = []

        # Extract from user message (higher reliability)
        user_facts = self.extract(user_message)
        for f in user_facts:
            f.confidence = min(1.0, f.confidence * 1.2)  # Boost user statements
            facts.append(f)

        # Extract from assistant response (lower reliability)
        assistant_facts = self.extract(assistant_response)
        for f in assistant_facts:
            f.confidence *= 0.8  # Reduce confidence for AI-generated content
            facts.append(f)

        # De-duplicate by content similarity
        return self._deduplicate(facts)

    def _split_sentences(self, text: str) -> list[str]:
        """Split text into sentences."""
        # Simple sentence splitting
        sentences = re.split(r'[.!?]+\s+|\n+', text)
        return [s.strip() for s in sentences if s.strip()]

    def _classify_sentence(self, sentence: str) -> MemoryType:
        """Classify a single sentence into a memory type."""
        s = sentence.lower()

        # Procedural indicators
        if any(kw in s for kw in [
            "how to", "step ", "install", "run ", "execute",
            "command", "```", "def ", "class ", "import ",
        ]):
            return MemoryType.PROCEDURAL

        # Episodic indicators
        if any(kw in s for kw in [
            "yesterday", "today", "last week", "happened",
            "did", "went", "meeting", "session", "talked",
        ]):
            return MemoryType.EPISODIC

        # Default to semantic (factual statements)
        return MemoryType.SEMANTIC

    def _estimate_confidence(self, sentence: str) -> float:
        """Estimate confidence for an extracted fact."""
        s = sentence.lower()

        # High confidence indicators
        if any(kw in s for kw in ["always", "never", "definitely", "certainly"]):
            return 0.90

        # Medium confidence indicators
        if any(kw in s for kw in ["usually", "often", "prefers", "likes"]):
            return 0.80

        # Low confidence indicators
        if any(kw in s for kw in ["maybe", "might", "perhaps", "sometimes"]):
            return 0.60

        # Default
        return 0.75

    def _deduplicate(self, facts: list[ExtractedFact]) -> list[ExtractedFact]:
        """Remove duplicate facts based on content similarity."""
        seen_content: set[str] = set()
        unique: list[ExtractedFact] = []

        for fact in facts:
            # Normalize for comparison
            normalized = fact.content.lower().strip()
            if normalized not in seen_content:
                seen_content.add(normalized)
                unique.append(fact)

        return unique
