from pathlib import Path
import sys,time,json,uno
from com.sun.star.beans import PropertyValue

ROOT=Path(__file__).resolve().parents[1]
OUT=ROOT/'ESP_00VB_Livrables'
def prop(n,v):
    p=PropertyValue();p.Name=n;p.Value=v;return p
def connect():
    local=uno.getComponentContext()
    resolver=local.ServiceManager.createInstanceWithContext('com.sun.star.bridge.UnoUrlResolver',local)
    for i in range(10):
        try:
            ctx=resolver.resolve('uno:socket,host=127.0.0.1,port=2017;urp;StarOffice.ComponentContext')
            return ctx.ServiceManager.createInstanceWithContext('com.sun.star.frame.Desktop',ctx)
        except Exception:time.sleep(.5)
    raise RuntimeError('Connexion LibreOffice indisponible')

def check():
    desktop=connect()
    doc=desktop.loadComponentFromURL((OUT/'03_Gestion_organisme.xlsx').as_uri(),'_blank',0,(prop('Hidden',True),prop('ReadOnly',False),prop('UpdateDocMode',0)))
    doc.calculateAll()
    errors=[]
    for sh in doc.Sheets:
        cursor=sh.createCursor();cursor.gotoEndOfUsedArea(False);r=cursor.RangeAddress
        for row in range(r.EndRow+1):
            for col in range(r.EndColumn+1):
                cell=sh.getCellByPosition(col,row)
                if cell.Error:errors.append([sh.Name,cell.AbsoluteName,cell.Formula,cell.Error,cell.String])
    result={'engine':'LibreOffice Calc, recalcul réel; Excel non installé','errors':errors,'tests':[]}
    def case(name,got,expected):result['tests'].append({'test':name,'observed':got,'expected':expected,'passed':got==expected})
    for row in range(1,10):case('Calcul rabais '+str(row),doc.Sheets['Tests'].getCellByPosition(11,row).String,'RÉUSSI')
    for cell,exp in [('E18',21.25),('F18',2545),('G18',23.75),('H18',2521.25),('E14',10.75),('H14',1251.25),('B28',1)]:case('Portrait '+cell,doc.Sheets['Portrait'].getCellRangeByName(cell).Value,exp)
    for row,exp in [(1,'Oui'),(2,'Oui'),(5,'Non'),(6,'Non'),(7,'Oui'),(9,'Oui')]:case('Admissibilité C%03d'%row,doc.Sheets['Clients'].getCellByPosition(10,row).String,exp)
    case('Une ligne par destinataire',sum(doc.Sheets['Fusion'].getCellByPosition(8,row).String=='Oui' for row in range(1,51)),12)
    # Mutation d’une consultation : rapprochement jusqu’au portrait et à la facture.
    sh=doc.Sheets['RendezVous'];old=sh.getCellRangeByName('H15').Value
    sh.getCellRangeByName('H15').Value=2;doc.calculateAll()
    case('Heures modifiées -> net RDV',sh.getCellRangeByName('O15').Value,190)
    case('Heures modifiées -> facture',doc.Sheets['Facture'].getCellRangeByName('C21').Value,190)
    case('Heures modifiées -> portrait',doc.Sheets['Portrait'].getCellRangeByName('H18').Value,2616.25)
    sh.getCellRangeByName('H15').Value=old;doc.calculateAll()
    old=sh.getCellRangeByName('A16').String;sh.getCellRangeByName('A16').String='R014';doc.calculateAll()
    case('Doublon RDV détecté',sh.getCellRangeByName('S16').String,'DOUBLON RDV');sh.getCellRangeByName('A16').String=old
    old=sh.getCellRangeByName('C16').String;sh.getCellRangeByName('C16').String='C999';doc.calculateAll()
    case('Client inconnu détecté',sh.getCellRangeByName('S16').String,'Client à corriger');sh.getCellRangeByName('C16').String=old
    doc.calculateAll()
    sh.getCellRangeByName('T24').String='Durée prolongée confirmée par la professionnelle — essai'
    doc.calculateAll()
    case('Écart documenté -> alerte levée',sh.getCellRangeByName('S24').String,'OK')
    case('Heures prévues conservées',sh.getCellRangeByName('G24').Value,1)
    case('Heures réelles conservées',sh.getCellRangeByName('H24').Value,1.25)
    sh.getCellRangeByName('T24').String='';doc.calculateAll()
    result['passed']=not errors and all(t['passed'] for t in result['tests'])
    (ROOT/'_fabrication_00VB'/'verification.json').write_text(json.dumps(result,ensure_ascii=False,indent=2),encoding='utf-8')
    # Export multi-feuilles d’origine pour inspection visuelle, sans enregistrer le XLSX dans Calc.
    doc.storeToURL((ROOT/'_fabrication_00VB'/'Classeur_apercu.pdf').as_uri(),(prop('FilterName','calc_pdf_Export'),prop('Overwrite',True)))
    doc.close(True)
    print(json.dumps(result,ensure_ascii=False))

if __name__=='__main__':check()
