from __future__ import annotations
import re, unicodedata
from difflib import SequenceMatcher
from sqlalchemy import select
from sqlalchemy.orm import Session
from app.models.entities import MasterEntity, MasterSourceRecord, SurvivorshipRule

def clean_text(value):
    value=unicodedata.normalize('NFKD',str(value or '')).encode('ascii','ignore').decode().lower().strip()
    return re.sub(r'[^a-z0-9]+',' ',value).strip()

def clean_tax(value): return re.sub(r'\D','',str(value or ''))

def normalize(data:dict)->dict:
    out=dict(data or {})
    for key in ['legal_name','display_name','name','city','address']: 
        if key in out: out[key]=clean_text(out[key])
    if 'tax_id' in out: out['tax_id']=clean_tax(out['tax_id'])
    if 'email' in out: out['email']=str(out['email']).strip().lower()
    return out

def similarity(a,b): return int(round(100*SequenceMatcher(None,clean_text(a),clean_text(b)).ratio()))

def match_score(left:dict,right:dict,fields:list[str],weights:dict,exact_fields:list[str]):
    evidence={}; total=0; used=0
    for field in fields:
        lv,rv=left.get(field),right.get(field)
        if lv in (None,'') or rv in (None,''): continue
        weight=int(weights.get(field,1)); score=100 if field in exact_fields and str(lv)==str(rv) else similarity(lv,rv)
        evidence[field]={'left':lv,'right':rv,'score':score,'weight':weight}; total+=score*weight; used+=weight
    return (int(total/used) if used else 0),evidence

def rebuild_golden(db:Session,entity:MasterEntity):
    rows=db.scalars(select(MasterSourceRecord).where(MasterSourceRecord.master_entity_id==entity.id,MasterSourceRecord.active.is_(True)).order_by(MasterSourceRecord.source_priority)).all()
    rules=db.scalars(select(SurvivorshipRule).where(SurvivorshipRule.entity_type==entity.entity_type,SurvivorshipRule.enabled.is_(True))).all()
    data={}; fields=set().union(*(set((r.normalized_data or {}).keys()) for r in rows)) if rows else set()
    rule_map={r.field_name:r for r in rules}
    for field in fields:
        values=[(r.source_system,(r.source_data or {}).get(field),(r.normalized_data or {}).get(field),r.source_priority,r.last_seen_at) for r in rows if (r.normalized_data or {}).get(field) not in (None,'')]
        if not values: continue
        rule=rule_map.get(field); strategy=rule.strategy if rule else 'source_priority'
        if strategy=='most_recent': chosen=sorted(values,key=lambda x:x[4],reverse=True)[0]
        elif strategy=='longest': chosen=max(values,key=lambda x:len(str(x[1] or '')))
        elif strategy=='source_order' and rule and rule.source_order:
            order={name:i for i,name in enumerate(rule.source_order)}; chosen=min(values,key=lambda x:order.get(x[0],9999))
        else: chosen=min(values,key=lambda x:x[3])
        data[field]=chosen[1]
    entity.golden_data=data; entity.source_count=len(rows)
    required=[r.field_name for r in rules if r.required]; complete=sum(1 for f in required if data.get(f) not in (None,''))
    entity.quality_score=100 if not required else int(100*complete/len(required))
    entity.legal_name=str(data.get('legal_name') or data.get('name') or entity.legal_name or '')[:300]
    entity.display_name=str(data.get('display_name') or data.get('trade_name') or entity.legal_name or '')[:300]
    entity.tax_id=clean_tax(data.get('tax_id') or entity.tax_id)
    return entity
