from __future__ import annotations
import re, unicodedata
from urllib.parse import urlparse
from dataclasses import dataclass, asdict

CONDITION_NEW='new'
CONDITION_REFURBISHED='refurbished'
CONDITION_OPEN_BOX='open_box'
CONDITION_USED='used'
VALID_CONDITIONS={CONDITION_NEW,CONDITION_REFURBISHED,CONDITION_OPEN_BOX,CONDITION_USED}

LABELS={
    CONDITION_NEW:'Sıfır',
    CONDITION_REFURBISHED:'Yenilenmiş',
    CONDITION_OPEN_BOX:'Açık kutu / Teşhir',
    CONDITION_USED:'İkinci el',
    'unknown':'Bilinmiyor',
    'conflict':'Çelişkili',
}

ALIASES={
    'new':CONDITION_NEW,'sifir':CONDITION_NEW,'sıfır':CONDITION_NEW,'yeni':CONDITION_NEW,
    'refurbished':CONDITION_REFURBISHED,'renewed':CONDITION_REFURBISHED,'yenilenmis':CONDITION_REFURBISHED,'yenilenmiş':CONDITION_REFURBISHED,
    'open_box':CONDITION_OPEN_BOX,'openbox':CONDITION_OPEN_BOX,'acik_kutu':CONDITION_OPEN_BOX,'açık_kutu':CONDITION_OPEN_BOX,'teshir':CONDITION_OPEN_BOX,'teşhir':CONDITION_OPEN_BOX,
    'used':CONDITION_USED,'second_hand':CONDITION_USED,'ikinci_el':CONDITION_USED,'ikinci el':CONDITION_USED,
}

# Outlet tek başına bilinçli olarak yoktur: bazı mağazalarda sıfır outlet ürünü de bulunur.
PATTERNS={
    CONDITION_REFURBISHED:[
        r'\byenilenmis(?:\s+urun)?\b', r'\brefurbished(?:condition)?\b', r'\brenewed\b',
        r'\breconditioned\b', r'\bcertified\s+refurbished\b', r'\brevize\b', r'\brevizyonlu\b',
        r'\brefurb\b',
    ],
    CONDITION_OPEN_BOX:[
        r'\bacik\s+kutu\b', r'\bopen\s*box\b', r'\bteshir(?:\s+urunu)?\b', r'\bdisplay\s+unit\b',
        r'\bshowroom\s+unit\b',
    ],
    CONDITION_USED:[
        r'\bikinci\s+el\b', r'\b2\s*el\b', r'\bused(?:condition)?\b', r'\bpre\s*owned\b',
        r'\bpreowned\b', r'\bsecond\s+hand\b',
    ],
    CONDITION_NEW:[
        r'\bnewcondition\b', r'\bschema\s+org\s+newcondition\b', r'\bitemcondition\s+newcondition\b',
        r'\bsifir\s+urun\b', r'\bkapali\s+kutu\b', r'\bbrand\s+new\b', r'\bfactory\s+sealed\b',
    ],
}


# Bu domainlerde normal perakende sayfası varsayılan olarak sıfır üründür; ancak
# yenilenmiş/açık-kutu/ikinci-el sinyali her zaman bu varsayımı ezer. Böylece
# piyasa sitelerinde itemCondition eksikliği yeni ürün fiyatlarını sonsuza dek
# pending bırakmazken kondisyon karışmasına da izin verilmez.
TRUSTED_NEW_RETAIL_DOMAINS={
    'hepsiburada.com','teknosa.com','mediamarkt.com.tr','vatanbilgisayar.com',
    'apple.com','samsung.com','mi.com','xiaomi.com','oppo.com','honor.com','realme.com',
}

def _host(url:str)->str:
    try:
        h=(urlparse(url or '').hostname or '').lower()
        return h[4:] if h.startswith('www.') else h
    except Exception:return ''

def _trusted_new_retail(url:str)->str|None:
    h=_host(url)
    for d in TRUSTED_NEW_RETAIL_DOMAINS:
        if h==d or h.endswith('.'+d):return d
    return None

@dataclass
class ConditionAssessment:
    expected: str
    detected: str | None
    status: str
    confidence: float
    evidence: str
    signals: list[str]
    matched_types: list[str]

    def to_dict(self):
        return asdict(self)


def _norm(value:str) -> str:
    s=(value or '').casefold().replace('ı','i').replace('İ','i')
    s=''.join(c for c in unicodedata.normalize('NFKD',s) if not unicodedata.combining(c))
    return re.sub(r'[^a-z0-9]+',' ',s).strip()


def normalize_condition(value) -> str:
    raw=str(value or '').strip()
    if not raw:return CONDITION_NEW
    key=raw.casefold().strip().replace('-','_')
    if key in VALID_CONDITIONS:return key
    nk=_norm(raw).replace(' ','_')
    return ALIASES.get(raw.casefold(),ALIASES.get(nk,CONDITION_NEW))


def condition_label(value) -> str:
    return LABELS.get(normalize_condition(value),LABELS['unknown'])


def detect_condition(text:str='', url:str='', condition_text:str='') -> tuple[str|None,float,str,list[str],list[str]]:
    # condition_text (JSON-LD itemCondition vb.) önce gelir, sonra başlık/url.
    fields=[('structured',condition_text or ''),('text',text or ''),('url',url or '')]
    hits={k:[] for k in VALID_CONDITIONS}
    for source,raw in fields:
        n=_norm(raw)
        if not n:continue
        for ctype,patterns in PATTERNS.items():
            for pat in patterns:
                m=re.search(pat,n,re.I)
                if m:
                    token=m.group(0).strip()
                    item=f'{source}:{token}'
                    if item not in hits[ctype]:hits[ctype].append(item)
    types=[k for k,v in hits.items() if v]
    # Non-new sinyal her zaman genel/structured "new" sinyalinden daha güvenlidir.
    nonnew=[x for x in types if x!=CONDITION_NEW]
    if len(nonnew)>1:
        sig=[x for t in nonnew for x in hits[t]][:8]
        return None,0.99,'; '.join(sig),sig,sorted(types)
    if len(nonnew)==1:
        t=nonnew[0];sig=(hits[t]+hits[CONDITION_NEW])[:8]
        # Yenilenmiş/açık kutu/ikinci el kelimesi varsa schema NewCondition yanlış olsa bile non-new kazanır,
        # fakat kanıt notuna çelişkiyi ekler.
        conf=.995 if any(x.startswith('structured:') for x in hits[t]) else .98
        return t,conf,'; '.join(sig),sig,sorted(types)
    if CONDITION_NEW in types:
        sig=hits[CONDITION_NEW][:8]
        conf=.995 if any(x.startswith('structured:') for x in sig) else .96
        return CONDITION_NEW,conf,'; '.join(sig),sig,types
    return None,0.0,'',[],[]


def assess_condition(product,text:str='',url:str='',condition_text:str='',require_known:bool=True) -> ConditionAssessment:
    expected=normalize_condition((product or {}).get('condition_type','new') if hasattr(product,'get') else 'new')
    detected,conf,evidence,signals,types=detect_condition(text=text,url=url,condition_text=condition_text)
    if detected is None:
        if len([x for x in types if x!=CONDITION_NEW])>1:
            return ConditionAssessment(expected,None,'conflict',.99,evidence,signals,types)
        # v0.3.9: Güvenilir normal perakende domaininde explicit non-new sinyali
        # yoksa sıfır ürün için kontrollü fallback. Marketplace dışı/şüpheli
        # domainlerde unknown kuralı aynen katıdır.
        trusted=_trusted_new_retail(url) if expected==CONDITION_NEW else None
        if trusted:
            ev=f'trusted-retail-new-default:{trusted}; non-new sinyali yok'
            return ConditionAssessment(expected,CONDITION_NEW,'verified',.93,ev,[ev],[CONDITION_NEW])
        return ConditionAssessment(expected,None,'unknown',0.0,evidence,signals,types)
    if detected!=expected:
        return ConditionAssessment(expected,detected,'mismatch',conf,evidence,signals,types)
    return ConditionAssessment(expected,detected,'verified',conf,evidence,signals,types)


def validate_condition(product,text:str='',url:str='',condition_text:str='',require_known:bool=True) -> ConditionAssessment:
    a=assess_condition(product,text,url,condition_text,require_known=require_known)
    if a.status=='verified':return a
    if a.status=='unknown' and not require_known:return a
    if a.status=='unknown':
        raise ValueError(f'CONDITION_UNKNOWN: Ürün durumu doğrulanamadı; beklenen {condition_label(a.expected)}. Fiyat yayına alınmadı.')
    if a.status=='conflict':
        raise ValueError(f'CONDITION_CONFLICT: Ürün durumu sinyalleri çelişkili ({a.evidence or "kanıt yok"}). Fiyat yayına alınmadı.')
    raise ValueError(f'CONDITION_MISMATCH: Beklenen {condition_label(a.expected)}, bulunan {condition_label(a.detected)}. Kanıt: {a.evidence or "durum sinyali"}')
