#!/usr/bin/env python3
"""
Query Expander - Sorgu Genişletme Modülü
========================================
Türkçe sorguları sözlükten besleyerek İngilizce terimlere genişletir.

Özellikler:
- Türkçe teknik terimleri İngilizce karşılıklarıyla zenginleştirir
- Yazım hatalarını fuzzy matching ile düzeltir
- Usta jargonunu teknik terminolojiye çevirir

Kullanım:
    from query_expander import QueryExpander
    
    expander = QueryExpander()
    result = expander.expand("yürüyüş motoru basınc ayarı")
    # {
    #     "original": "yürüyüş motoru basınc ayarı",
    #     "corrected": "yürüyüş motoru basınç ayarı",
    #     "turkish_terms": ["yürüyüş", "motor", "basınç", "ayar"],
    #     "english_terms": ["travel", "propel", "motor", "pressure", "adjustment"],
    #     "expanded_query": "travel propel motor pressure adjustment yürüyüş basınç",
    #     "term_mappings": {"yürüyüş": ["travel", "propel"], "basınç": ["pressure", "bar"]}
    # }
"""

import os
import re
import sqlite3
from typing import Dict, List, Set, Tuple, Optional
from dataclasses import dataclass, field
from difflib import SequenceMatcher


# ==================== CONFIGURATION ====================

DICTIONARY_PATH = os.getenv("DICTIONARY_PATH", "/mnt/pdfs/dictionary.db")
FUZZY_THRESHOLD = 0.75  # Benzerlik eşiği (0-1 arası)
MAX_ENGLISH_TERMS = 3   # Her Türkçe terim için max İngilizce karşılık


@dataclass
class ExpansionResult:
    """Sorgu genişletme sonucu"""
    original_query: str
    corrected_query: str
    turkish_terms: List[str]
    english_terms: List[str]
    expanded_query: str
    term_mappings: Dict[str, List[str]] = field(default_factory=dict)
    corrections: Dict[str, str] = field(default_factory=dict)
    confidence: float = 0.0
    
    def to_dict(self) -> Dict:
        return {
            "original": self.original_query,
            "corrected": self.corrected_query,
            "turkish_terms": self.turkish_terms,
            "english_terms": self.english_terms,
            "expanded_query": self.expanded_query,
            "term_mappings": self.term_mappings,
            "corrections": self.corrections,
            "confidence": self.confidence
        }


class QueryExpander:
    """
    Sorgu Genişletici
    
    Türkçe teknik terimleri sözlükten besleyerek İngilizce karşılıklarla zenginleştirir.
    """
    
    def __init__(self, dict_path: str = DICTIONARY_PATH):
        """
        Args:
            dict_path: dictionary.db dosya yolu
        """
        self.dict_path = dict_path
        self.conn = None
        self._connect()
        
        # Türkçe karakter düzeltme tablosu
        self.tr_char_map = {
            'i': ['ı', 'i', 'İ', 'I'],
            'ı': ['i', 'ı', 'I', 'İ'],
            'o': ['ö', 'o', 'O', 'Ö'],
            'ö': ['o', 'ö', 'O', 'Ö'],
            'u': ['ü', 'u', 'U', 'Ü'],
            'ü': ['u', 'ü', 'U', 'Ü'],
            'c': ['ç', 'c', 'C', 'Ç'],
            'ç': ['c', 'ç', 'C', 'Ç'],
            'g': ['ğ', 'g', 'G', 'Ğ'],
            'ğ': ['g', 'ğ', 'G', 'Ğ'],
            's': ['ş', 's', 'S', 'Ş'],
            'ş': ['s', 'ş', 'S', 'Ş'],
        }
        
        # Usta jargonu → teknik terim
        self.jargon_map = {
            # Genel
            "ötüyor": ["noise", "sound", "squealing"],
            "bayılıyor": ["stalling", "loss of power", "pressure drop"],
            "patır patır": ["knocking", "rattling"],
            "ısınıyor": ["overheating", "temperature"],
            "kaçırıyor": ["leak", "leakage"],
            "titriyor": ["vibration", "shaking"],
            "çalışmıyor": ["not working", "failure", "malfunction"],
            "takılıyor": ["stuck", "jamming"],
            "gevşemiş": ["loose", "slack"],
            "aşınmış": ["worn", "wear"],
            "kırık": ["broken", "crack", "fracture"],
            "sızıyor": ["leak", "seep"],
            "tıkalı": ["clogged", "blocked"],
            "yanmış": ["burned", "burnt", "damaged"],
            
            # Makine parçaları - Usta dili
            "bom": ["boom"],
            "kova": ["bucket"],
            "kepçe": ["bucket", "grab"],
            "arm": ["arm", "stick"],
            "yürüyüş": ["travel", "propel", "track"],
            "kule": ["swing", "slew"],
            "dönüş": ["swing", "rotation"],
            "şase": ["chassis", "frame"],
            "palet": ["track", "crawler"],
            "zincir": ["chain", "track chain"],
            "makara": ["roller", "idler"],
            "dişli": ["gear", "sprocket"],
        }
        
        # Sık kullanılan teknik terimler cache'i
        self._term_cache = {}
        self._load_frequent_terms()
    
    def _connect(self):
        """Veritabanına bağlan"""
        try:
            if os.path.exists(self.dict_path):
                self.conn = sqlite3.connect(self.dict_path)
                self.conn.row_factory = sqlite3.Row
                print(f"✓ Sözlük bağlantısı OK: {self.dict_path}")
            else:
                print(f"⚠️ Sözlük bulunamadı: {self.dict_path}")
                self.conn = None
        except Exception as e:
            print(f"❌ Sözlük bağlantı hatası: {e}")
            self.conn = None
    
    def _load_frequent_terms(self):
        """Sık kullanılan KALİTELİ terimleri cache'e yükle"""
        if not self.conn:
            return
        
        try:
            cursor = self.conn.cursor()
            
            # Sadece kaliteli kategorilerdeki, temiz verileri al
            cursor.execute('''
                SELECT canonical_tr, canonical_en 
                FROM technical_terms 
                WHERE category IN ('parts-catalog', 'katalog-detailed', 'equipment', 'parts', 'maden', 'dynapac', 'compound')
                AND canonical_tr IS NOT NULL 
                AND canonical_en IS NOT NULL
                AND LENGTH(canonical_tr) BETWEEN 2 AND 40
                AND LENGTH(canonical_en) BETWEEN 2 AND 40
                AND canonical_en NOT LIKE '%(%'
                AND canonical_en NOT LIKE '%[%'
                AND canonical_en NOT GLOB '*[0-9] [0-9]*'
                AND canonical_tr NOT LIKE '%(%'
                AND canonical_tr NOT LIKE '%[%'
            ''')
            
            for row in cursor.fetchall():
                tr = row['canonical_tr'].lower().strip()
                en = row['canonical_en'].lower().strip()
                
                # Sayı ağırlıklı değerleri atla
                if sum(c.isdigit() for c in en) > len(en) * 0.3:
                    continue
                if sum(c.isdigit() for c in tr) > len(tr) * 0.3:
                    continue
                
                if tr not in self._term_cache:
                    self._term_cache[tr] = set()
                self._term_cache[tr].add(en)
            
            # Reverse lookup için (EN -> TR)
            self._reverse_cache = {}
            for tr, en_set in self._term_cache.items():
                for en in en_set:
                    if en not in self._reverse_cache:
                        self._reverse_cache[en] = set()
                    self._reverse_cache[en].add(tr)
            
            print(f"✓ {len(self._term_cache):,} kaliteli terim cache'e yüklendi")
        except Exception as e:
            print(f"⚠️ Cache yükleme hatası: {e}")
    
    def _tokenize(self, text: str) -> List[str]:
        """Metni token'lara ayır"""
        # Türkçe karakterleri koru, noktalama işaretlerini temizle
        text = text.lower().strip()
        text = re.sub(r'[^\w\sğüşöçıİĞÜŞÖÇ]', ' ', text)
        tokens = text.split()
        return [t for t in tokens if len(t) >= 2]
    
    def _fuzzy_match(self, word: str, candidates: List[str]) -> Optional[Tuple[str, float]]:
        """Fuzzy matching ile en yakın kelimeyi bul"""
        best_match = None
        best_score = 0.0
        
        for candidate in candidates:
            score = SequenceMatcher(None, word.lower(), candidate.lower()).ratio()
            if score > best_score and score >= FUZZY_THRESHOLD:
                best_score = score
                best_match = candidate
        
        return (best_match, best_score) if best_match else None
    
    def _correct_spelling(self, word: str) -> Tuple[str, bool]:
        """
        Yazım hatalarını düzelt.
        
        Returns:
            (düzeltilmiş_kelime, düzeltme_yapıldı_mı)
        """
        word_lower = word.lower()
        
        # Önce cache'de tam eşleşme var mı? O zaman düzeltme gereksiz
        if word_lower in self._term_cache:
            return word, False
        
        # Kısa kelimeleri (3 karakter altı) düzeltme
        if len(word) < 3:
            return word, False
        
        # Marka/Model gibi kısaltmaları düzeltme (JCB, PC200, vb.)
        if word.isupper() or any(c.isdigit() for c in word):
            return word, False
        
        # Türkçe karakter düzeltmesi
        # "basınc" → "basınç", "hidrolik" → "hidrolik"
        common_mistakes = {
            'basınc': 'basınç',
            'basinc': 'basınç',
            'pomba': 'pompa',
            'valv': 'valf',
            'hidraulik': 'hidrolik',
            'silndir': 'silindir',
            'silindır': 'silindir',
            'motör': 'motor',
            'filre': 'filtre',
            'sekman': 'segman',
            'cıvıta': 'cıvata',
        }
        
        if word_lower in common_mistakes:
            return common_mistakes[word_lower], True
        
        # Çok benzer terimleri ara (yüksek eşik ile)
        candidates = [k for k in self._term_cache.keys() if abs(len(k) - len(word_lower)) <= 2]
        match = self._fuzzy_match(word, candidates)
        if match and match[1] >= 0.85:  # Yüksek güven eşiği
            return match[0], True
        
        return word, False
    
    def _find_english_terms(self, turkish_word: str) -> List[str]:
        """
        Türkçe kelime için İngilizce karşılıkları bul.
        
        Öncelik:
        1. Usta jargonu (kesin eşleşme)
        2. Cache'de tam eşleşme
        3. Cache'de kelime başlangıcı eşleşme
        """
        word_lower = turkish_word.lower().strip()
        english_terms = []
        seen = set()
        
        def add_term(term):
            """Tekrarsız ve kaliteli terim ekle"""
            term = term.lower().strip()
            if term not in seen and len(term) >= 2:
                # Çok uzun ve karmaşık terimleri atla
                if len(term) <= 35 and term.count(' ') <= 3:
                    seen.add(term)
                    english_terms.append(term)
        
        # 1. Usta jargonu (en yüksek öncelik)
        if word_lower in self.jargon_map:
            for term in self.jargon_map[word_lower]:
                add_term(term)
        
        # 2. Cache'de tam eşleşme
        if word_lower in self._term_cache:
            for term in sorted(self._term_cache[word_lower], key=len):
                add_term(term)
        
        # 3. Cache'de kelime başlangıcı eşleşme (hidrolik -> hydraulic)
        if len(english_terms) < MAX_ENGLISH_TERMS:
            for tr_term, en_set in self._term_cache.items():
                if tr_term.startswith(word_lower) or word_lower.startswith(tr_term):
                    for term in sorted(en_set, key=len):
                        add_term(term)
                        if len(english_terms) >= MAX_ENGLISH_TERMS:
                            break
                if len(english_terms) >= MAX_ENGLISH_TERMS:
                    break
        
        return english_terms[:MAX_ENGLISH_TERMS]
    
    def expand(self, query: str) -> ExpansionResult:
        """
        Sorguyu genişlet.
        
        Args:
            query: Kullanıcı sorgusu (Türkçe, hatalar olabilir)
            
        Returns:
            ExpansionResult nesnesi
        """
        if not query:
            return ExpansionResult(
                original_query="",
                corrected_query="",
                turkish_terms=[],
                english_terms=[],
                expanded_query=""
            )
        
        original_query = query.strip()
        tokens = self._tokenize(original_query)
        
        # Sonuç değişkenleri
        corrected_tokens = []
        corrections = {}
        turkish_terms = []
        all_english_terms = set()
        term_mappings = {}
        
        # Her token'ı işle
        for token in tokens:
            # 1. Yazım düzeltme
            corrected, was_corrected = self._correct_spelling(token)
            corrected_tokens.append(corrected)
            
            if was_corrected:
                corrections[token] = corrected
            
            # 2. İngilizce karşılıkları bul
            english_terms = self._find_english_terms(corrected)
            
            if english_terms:
                turkish_terms.append(corrected)
                term_mappings[corrected] = english_terms
                all_english_terms.update(english_terms)
        
        # Düzeltilmiş sorgu
        corrected_query = ' '.join(corrected_tokens)
        
        # Genişletilmiş sorgu: Türkçe + İngilizce (arama için)
        english_list = list(all_english_terms)
        expanded_query = f"{corrected_query} {' '.join(english_list)}".strip()
        
        # Güven skoru hesapla
        confidence = len(term_mappings) / max(len(tokens), 1)
        
        return ExpansionResult(
            original_query=original_query,
            corrected_query=corrected_query,
            turkish_terms=turkish_terms,
            english_terms=english_list,
            expanded_query=expanded_query,
            term_mappings=term_mappings,
            corrections=corrections,
            confidence=min(confidence, 1.0)
        )
    
    def translate_to_english(self, query: str) -> str:
        """
        Sorguyu tamamen İngilizceye çevir (AI'a göndermek için).
        
        Sözlükte olmayan kelimeler korunur.
        """
        result = self.expand(query)
        
        # Eğer İngilizce terim bulunduysa sadece onları kullan
        if result.english_terms:
            return ' '.join(result.english_terms)
        
        # Bulunamadıysa orijinal sorguyu döndür
        return result.corrected_query
    
    def translate_to_turkish(self, english_text: str) -> str:
        """
        İngilizce metni Türkçeye çevir (AI cevabını işlemek için).
        
        Sadece teknik terimleri çevirir, gramer korur.
        Daha karmaşık çeviri için AI kullanılmalı.
        """
        if not hasattr(self, '_reverse_cache') or not self._reverse_cache:
            return english_text
        
        result = english_text
        
        # Uzun terimlerden kısa terimlere doğru replace yap
        # Bu şekilde "relief valve" önce "emniyet valfi" olur
        sorted_terms = sorted(self._reverse_cache.keys(), key=len, reverse=True)
        
        for en_term in sorted_terms:
            tr_terms = self._reverse_cache[en_term]
            if not tr_terms:
                continue
            
            # En kısa Türkçe karşılığı seç (genelde en uygun olanı)
            tr_term = min(tr_terms, key=len)
            
            # Case-insensitive replace
            pattern = re.compile(r'\b' + re.escape(en_term) + r'\b', re.IGNORECASE)
            if pattern.search(result):
                result = pattern.sub(tr_term, result)
        
        return result
    
    def get_term_tooltip(self, english_term: str) -> Optional[str]:
        """
        İngilizce terim için Türkçe tooltip al.
        
        UI'da İngilizce kelimenin altını çizmek için kullanılır.
        """
        if not self.conn:
            return None
        
        try:
            cursor = self.conn.cursor()
            cursor.execute('''
                SELECT canonical_tr FROM technical_terms 
                WHERE LOWER(canonical_en) = ? 
                AND canonical_tr IS NOT NULL
                LIMIT 1
            ''', (english_term.lower(),))
            
            row = cursor.fetchone()
            return row['canonical_tr'] if row else None
        
        except Exception:
            return None
    
    def close(self):
        """Bağlantıyı kapat"""
        if self.conn:
            self.conn.close()
            self.conn = None


# ==================== TEST ====================

def test_expander():
    """Test fonksiyonu"""
    expander = QueryExpander()
    
    test_queries = [
        "yürüyüş motoru basınc ayarı",         # Yazım hatası + teknik terim
        "bom silindiri keçesi değişimi",        # Usta jargonu
        "hidrolik pompa ötüyor",                # Ses problemi
        "jcb 330 kepçe silindir kaçırıyor",     # Marka + jargon
        "komatsu pc200 motor ısınıyor",         # Isı problemi
        "palet zinciri aşınmış",                # Aşınma
        "kule dönüş yavaş",                     # Swing problemi
    ]
    
    print("\n" + "=" * 70)
    print("   🔤 QUERY EXPANDER TEST")
    print("=" * 70)
    
    for query in test_queries:
        result = expander.expand(query)
        
        print(f"\n📝 Orijinal: {result.original_query}")
        
        if result.corrections:
            print(f"   ✏️ Düzeltme: {result.corrections}")
        
        print(f"   🔍 Düzeltilmiş: {result.corrected_query}")
        print(f"   🇹🇷 Türkçe: {result.turkish_terms}")
        print(f"   🇬🇧 İngilizce: {result.english_terms}")
        print(f"   📊 Eşleşmeler: {result.term_mappings}")
        print(f"   🎯 Genişletilmiş: {result.expanded_query}")
        print(f"   💯 Güven: {result.confidence:.0%}")
    
    # Çeviri testi
    print("\n" + "=" * 70)
    print("   🔄 ÇEVİRİ TESTİ")
    print("=" * 70)
    
    english_text = "Check the relief valve pressure and adjust the adjusting screw"
    turkish = expander.translate_to_turkish(english_text)
    print(f"\n🇬🇧 İngilizce: {english_text}")
    print(f"🇹🇷 Türkçe: {turkish}")
    
    expander.close()


if __name__ == "__main__":
    test_expander()

