"""QuranValidator - Validate and verify Quranic verses in text."""

from __future__ import annotations

import json
from pathlib import Path

from .normalizer import normalize_arabic, contains_arabic, extract_arabic_segments, NormalizeOptions
from .types import (
    QuranVerse,
    QuranSurah,
    ValidationResult,
    DetectionResult,
    DetectionSegment,
    ValidatorOptions,
    FabricationAnalysis,
    FabricationStats,
    WordAnalysis,
    MatchType,
)

_DATA_DIR = Path(__file__).parent / "data"


def _normalize_fabrication(text: str) -> str:
    """Aggressive normalization for fabrication checking using stripHamza option."""
    return normalize_arabic(text, NormalizeOptions(strip_hamza=True))


def _load_verses() -> list[dict]:
    with open(_DATA_DIR / "quran-verses.min.json", encoding="utf-8") as f:
        return json.load(f)


def _load_surahs() -> list[dict]:
    with open(_DATA_DIR / "quran-surahs.min.json", encoding="utf-8") as f:
        return json.load(f)


def _dict_to_verse(d: dict) -> QuranVerse:
    return QuranVerse(
        id=d["id"],
        surah=d["surah"],
        ayah=d["ayah"],
        text=d["text"],
        text_simple=d.get("textSimple", ""),
        page=d.get("page", 0),
        juz=d.get("juz", 0),
    )


def _dict_to_surah(d: dict) -> QuranSurah:
    return QuranSurah(
        number=d["number"],
        name=d["name"],
        english_name=d["englishName"],
        verses_count=d["versesCount"],
        revelation_type=d["revelationType"],
    )


class QuranValidator:
    """Validate and verify Quranic verses in text.

    Example::

        validator = QuranValidator()

        result = validator.validate("بِسْمِ ٱللَّهِ ٱلرَّحْمَٰنِ ٱلرَّحِيمِ")
        print(result.is_valid)   # True
        print(result.reference)  # "1:1"
    """

    def __init__(self, options: ValidatorOptions | None = None):
        opts = options or ValidatorOptions()
        self._max_suggestions = opts.max_suggestions
        self._min_detection_length = opts.min_detection_length

        # Load data
        self._verses = [_dict_to_verse(d) for d in _load_verses()]
        self._surahs = [_dict_to_surah(d) for d in _load_surahs()]

        # Build lookup maps
        self._verse_by_id: dict[int, QuranVerse] = {}
        self._verse_by_surah_ayah: dict[str, QuranVerse] = {}
        self._exact_verse_map: dict[str, QuranVerse] = {}
        self._normalized_verse_map: dict[str, list[QuranVerse]] = {}

        corpus_texts: list[str] = []

        for verse in self._verses:
            self._verse_by_id[verse.id] = verse
            self._verse_by_surah_ayah[f"{verse.surah}:{verse.ayah}"] = verse
            self._exact_verse_map[verse.text] = verse

            normalized = _normalize_fabrication(verse.text)
            self._normalized_verse_map.setdefault(normalized, []).append(verse)
            corpus_texts.append(normalized)

        # Concatenated corpus for fabrication detection
        self._normalized_corpus = " ".join(corpus_texts)

    def validate(self, text: str) -> ValidationResult:
        """Validate a potential Quran quote.

        Args:
            text: The Arabic text to validate.

        Returns:
            Validation result with match details.
        """
        trimmed = text.strip()
        normalized_input = normalize_arabic(trimmed)
        lookup_key = _normalize_fabrication(trimmed)

        if not contains_arabic(trimmed):
            return self._no_match(normalized_input)

        # Step 1: Try exact match
        exact = self._exact_verse_map.get(trimmed)
        if exact:
            return self._create_result(exact, "exact", normalized_input)

        # Step 2: Try normalized match
        matches = self._normalized_verse_map.get(lookup_key)
        if matches:
            primary = matches[0]
            result = self._create_result(primary, "normalized", normalized_input)
            if len(matches) > 1:
                result.suggestions = [
                    {"verse": v, "reference": f"{v.surah}:{v.ayah}"}
                    for v in matches[:self._max_suggestions]
                ]
            return result

        return self._no_match(normalized_input)

    def validate_against(self, text: str, reference: str) -> ValidationResult:
        """Validate text against a specific verse reference.

        Args:
            text: The Arabic text to validate.
            reference: The expected verse reference (e.g., "1:1" or "2:255-257").

        Returns:
            Validation result with diff information.
        """
        import re as _re

        trimmed = text.strip()
        normalized_input = normalize_arabic(trimmed)

        m = _re.match(r"^(\d+):(\d+)(?:-(\d+))?$", reference)
        if not m:
            return self._no_match(normalized_input)

        surah = int(m.group(1))
        start_ayah = int(m.group(2))
        end_ayah = int(m.group(3)) if m.group(3) else start_ayah

        if start_ayah == end_ayah:
            matched_verse = self.get_verse(surah, start_ayah)
            if not matched_verse:
                return self._no_match(normalized_input)
            expected_text = matched_verse.text
        else:
            vrange = self.get_verse_range(surah, start_ayah, end_ayah)
            if not vrange:
                return self._no_match(normalized_input)
            expected_text = vrange["text"]
            matched_verse = vrange["verses"][0]

        expected_normalized = normalize_arabic(expected_text)

        # Exact match
        if trimmed == expected_text:
            return ValidationResult(
                is_valid=True,
                match_type="exact",
                matched_verse=matched_verse,
                reference=reference,
                normalized_input=normalized_input,
                expected_normalized=expected_normalized,
            )

        # Normalized match
        input_lookup = _normalize_fabrication(trimmed)
        expected_lookup = _normalize_fabrication(expected_text)
        if input_lookup == expected_lookup:
            return ValidationResult(
                is_valid=True,
                match_type="normalized",
                matched_verse=matched_verse,
                reference=reference,
                normalized_input=normalized_input,
                expected_normalized=expected_normalized,
            )

        # No match
        mismatch_index = self._find_mismatch_index(input_lookup, expected_lookup)
        return ValidationResult(
            is_valid=False,
            match_type="none",
            reference=reference,
            normalized_input=normalized_input,
            expected_normalized=expected_normalized,
            mismatch_index=mismatch_index,
        )

    def detect_and_validate(self, text: str) -> DetectionResult:
        """Detect and validate all potential Quran quotes in text.

        Args:
            text: Text that may contain Quran quotes.

        Returns:
            Detection result with validated segments.
        """
        arabic_segments = extract_arabic_segments(text)
        if not arabic_segments:
            return DetectionResult(detected=False)

        validated = [
            DetectionSegment(
                text=seg.text,
                start_index=seg.start_index,
                end_index=seg.end_index,
                validation=self.validate(seg.text),
            )
            for seg in arabic_segments
            if len(seg.text) >= self._min_detection_length
        ]

        detected = any(s.validation and s.validation.is_valid for s in validated)
        return DetectionResult(detected=detected, segments=validated)

    def get_verse(self, surah: int, ayah: int) -> QuranVerse | None:
        """Get a verse by surah and ayah number."""
        return self._verse_by_surah_ayah.get(f"{surah}:{ayah}")

    def get_verse_range(
        self, surah: int, start_ayah: int, end_ayah: int
    ) -> dict | None:
        """Get a range of verses and concatenate their text.

        Returns:
            Dict with 'text', 'text_simple', and 'verses' keys, or None.
        """
        if start_ayah > end_ayah:
            return None

        verses: list[QuranVerse] = []
        for ayah in range(start_ayah, end_ayah + 1):
            verse = self.get_verse(surah, ayah)
            if not verse:
                return None
            verses.append(verse)

        return {
            "text": " ".join(v.text for v in verses),
            "text_simple": " ".join(v.text_simple for v in verses),
            "verses": verses,
        }

    def get_surah_verses(self, surah_number: int) -> list[QuranVerse]:
        """Get all verses in a surah."""
        return [v for v in self._verses if v.surah == surah_number]

    def get_surah(self, surah_number: int) -> QuranSurah | None:
        """Get surah information."""
        for s in self._surahs:
            if s.number == surah_number:
                return s
        return None

    def get_all_surahs(self) -> list[QuranSurah]:
        """Get all surahs."""
        return list(self._surahs)

    def search(
        self, query: str, limit: int = 10
    ) -> list[dict]:
        """Search verses by text (containment-based matching).

        Returns:
            List of {"verse": QuranVerse, "similarity": float} sorted by relevance.
        """
        normalized_query = normalize_arabic(query.strip())
        if not normalized_query:
            return []

        results: list[dict] = []
        for verse in self._verses:
            normalized_verse = normalize_arabic(verse.text)

            if normalized_query in normalized_verse:
                ratio = len(normalized_query) / len(normalized_verse)
                results.append({"verse": verse, "similarity": 0.7 + ratio * 0.3})
            elif normalized_verse in normalized_query:
                ratio = len(normalized_verse) / len(normalized_query)
                results.append({"verse": verse, "similarity": 0.5 + ratio * 0.3})

        results.sort(key=lambda r: r["similarity"], reverse=True)
        return results[:limit]

    def analyze_fabrication(self, text: str) -> FabricationAnalysis:
        """Analyze text for fabricated words that don't exist in the Quran.

        Uses greedy longest contiguous match algorithm.

        Args:
            text: The Arabic text to analyze.

        Returns:
            Analysis with word-by-word breakdown.
        """
        normalized_input = normalize_arabic(text)
        fabrication_normalized = _normalize_fabrication(text)
        words = normalized_input.split()
        fabrication_words = fabrication_normalized.split()
        word_results: list[WordAnalysis] = []

        if not words:
            return FabricationAnalysis(
                normalized_input=normalized_input,
                words=[],
                stats=FabricationStats(0, 0, 0.0),
            )

        i = 0
        while i < len(fabrication_words):
            # Binary search for longest contiguous match
            lo, hi, best = 1, len(fabrication_words) - i, 0

            while lo <= hi:
                mid = (lo + hi) // 2
                candidate = " ".join(fabrication_words[i:i + mid])
                if candidate in self._normalized_corpus:
                    best = mid
                    lo = mid + 1
                else:
                    hi = mid - 1

            if best > 0:
                for j in range(i, i + best):
                    word_results.append(WordAnalysis(word=words[j], is_fabricated=False))
                i += best
            else:
                word_results.append(WordAnalysis(word=words[i], is_fabricated=True))
                i += 1

        fabricated_count = sum(1 for w in word_results if w.is_fabricated)
        return FabricationAnalysis(
            normalized_input=normalized_input,
            words=word_results,
            stats=FabricationStats(
                total_words=len(word_results),
                fabricated_words=fabricated_count,
                fabricated_ratio=fabricated_count / len(word_results) if word_results else 0.0,
            ),
        )

    # --- Private helpers ---

    def _create_result(
        self, verse: QuranVerse, match_type: MatchType, normalized_input: str
    ) -> ValidationResult:
        return ValidationResult(
            is_valid=True,
            match_type=match_type,
            matched_verse=verse,
            reference=f"{verse.surah}:{verse.ayah}",
            normalized_input=normalized_input,
        )

    def _no_match(self, normalized_input: str | None = None) -> ValidationResult:
        return ValidationResult(
            is_valid=False,
            match_type="none",
            normalized_input=normalized_input,
        )

    def _find_mismatch_index(self, str1: str, str2: str) -> int:
        min_len = min(len(str1), len(str2))
        for i in range(min_len):
            if str1[i] != str2[i]:
                return i
        if len(str1) != len(str2):
            return min_len
        return -1


def create_validator(options: ValidatorOptions | None = None) -> QuranValidator:
    """Create a new QuranValidator instance."""
    return QuranValidator(options)
