import csv
import re
import os
from functools import lru_cache
from .lemmatizer import SindhiLemmatizer

class SindhiSpellchecker:
    def __init__(self, dict_path=None):
        if dict_path is None:
            base_path = os.path.dirname(__file__)
            dict_path = os.path.join(base_path, "data", "wordnet.csv")
        
        self.lemmatizer = SindhiLemmatizer(wordnet_path=dict_path)
        self.dictionary = set()
        self.SINDHI_LETTERS = list("ابپتٽثجڃچحخدڊذرڙزسشصضطظعغفقڪگلمنوهھءئي")
        self.CONFUSION_MAP = {
            "ھ": ["ح"], "ه": ["ھ"], "ز": ["ذ"],
            "س": ["ص"], "ڪ": ["ک"], "ي": ["ئ"]
        }
        self._load_dictionary(dict_path)

    def normalize(self, text):
        if not text: return ""
        return re.sub(r'[\u064B-\u0652\u0670]', '', str(text)).strip()

    def _load_dictionary(self, path):
        if not os.path.exists(path):
            return
        with open(path, encoding="utf-8-sig") as f:
            for row in csv.DictReader(f):
                w = self.normalize(row.get("word", ""))
                if w: self.dictionary.add(w)

    def _known(self, words):
        return set(w for w in words if w in self.dictionary)

    def _edits1(self, word):
        splits = [(word[:i], word[i:]) for i in range(len(word)+1)]
        deletes = [L + R[1:] for L, R in splits if R]
        transposes = [L + R[1] + R[0] + R[2:] for L, R in splits if len(R) > 1]
        replaces = [L + c + R[1:] for L, R in splits if R for c in self.SINDHI_LETTERS]
        inserts = [L + c + R for L, R in splits for c in self.SINDHI_LETTERS]
        return set(deletes + transposes + replaces + inserts)

    @lru_cache(maxsize=10000)
    def _generate_confusion_variants(self, word):
        variants = set()
        for i, char in enumerate(word):
            if char in self.CONFUSION_MAP:
                for replacement in self.CONFUSION_MAP[char]:
                    variants.add(word[:i] + replacement + word[i+1:])
        return variants

    def check(self, word):
        word_norm = self.normalize(word)
        if not word_norm: return {"correct": False, "suggestions": []}
        
        # 1. Direct match
        if word_norm in self.dictionary:
            return {"correct": True, "suggestions": []}

        # 2. Lemma match
        try:
            lemma = self.normalize(self.lemmatizer.lemmatize(word_norm))
            if lemma and lemma in self.dictionary:
                return {"correct": True, "suggestions": []}
        except:
            pass

        # 3. Confusion map variants
        candidates = self._known(self._generate_confusion_variants(word_norm))
        
        # 4. Edit distance 1
        if not candidates:
            candidates = self._known(self._edits1(word_norm))
            
        # 5. Edit distance 2 (only for short words <= 6 chars)
        if not candidates and len(word_norm) <= 6:
            candidates = set(
                e2 for e1 in self._edits1(word_norm)
                for e2 in self._edits1(e1)
                if e2 in self.dictionary
            )
            
        if candidates:
            # Sort by closeness (length difference)
            sorted_candidates = sorted(
                list(candidates), 
                key=lambda x: abs(len(x) - len(word_norm))
            )
            return {
                "correct": False, 
                "suggestions": sorted_candidates[:5]
            }

        return {"correct": False, "suggestions": []}