"""IMM5562 dynamic nested rows, exports and signed-template preservation."""
import hashlib
import json
import os
from pathlib import Path
import sys
import tempfile
import unittest
import pikepdf
from lxml import etree as E

BASE=Path(__file__).resolve().parents[2]

def packets(path):
    with pikepdf.open(path) as p:
        x=p.Root.AcroForm.XFA
        return {str(x[i]):x[i+1].read_bytes() for i in range(0,len(x),2)}

class Imm5562Test(unittest.TestCase):
    def test_dynamic_dependents_rows_and_preserved_packets(self):
        from imm5562_pdf import fill_travels,resolve
        from imm0008_xfa_checksum import form_checksum
        for lang,suffix in [('en','e'),('fr','f')]:
            c=json.loads((BASE/f'resources/imm/imm5562-contract.{lang}.json').read_text())
            blank=BASE/f'storage/app/imm/imm5562{suffix}.pdf'
            self.assertEqual(c['sha256'],hashlib.sha256(blank.read_bytes()).hexdigest())
            fields={c['identity']['family_name']:'CHEN',c['identity']['given_names']:'MARIA'}
            for group,counts in [('applicant',[7]),('spouse',[4]),('dependents',[5,0,6])]:
                definition=c['groups'][group]
                for dependent,count in enumerate(counts):
                    replace=lambda path,row=0:path.replace('{dependent}',str(dependent)).replace('{row}',str(row))
                    fields[replace(definition['selector'])]='0' if count else '1'
                    if definition['name']:fields[replace(definition['name'])]=f'MARIA CHEN {dependent}'
                    for row in range(count):
                        for key,meta in definition['fields'].items():
                            fields[replace(meta['path'],row)]={'from_date':'2024-06','to_date':'2024-07','duration':'10 days','destination':f'Paris, France {row}','purpose':'10','details':'Attended a family cultural ceremony'}[key]
            payload={'form':'IMM5562','language':lang,'fields':fields,'imm5562_rows':{'applicant':7,'spouse':4,'dependents':[5,0,6]},'imm5562_state':{'partner':True,'dependents':True}}
            with tempfile.TemporaryDirectory(dir=BASE/'storage/app/imm/tmp') as work:
                out=Path(work)/'draft.pdf';fill_travels(payload,out,blank)
                before,after=packets(blank),packets(out)
                self.assertTrue(out.read_bytes().startswith(blank.read_bytes()))
                for key,value in before.items():
                    if key not in ('datasets','form'):self.assertEqual(value,after[key],key)
                data=E.fromstring(after['datasets']);form=E.fromstring(after['form'])
                for path,value in fields.items():self.assertEqual(resolve(data,path).text,value,path)
                self.assertEqual(len(data.xpath('//*[local-name()="Item2X"]')),3)
                self.assertEqual(form.get('checksum'),form_checksum(after['template'],after['datasets']))
                self.assertEqual(form.xpath('//*[local-name()="exclGroup"]/*[local-name()="value"]'),[])
                self.assertFalse(form.xpath('//*[@access="readOnly" or @access="nonInteractive"]'))
                for group,counts in [('applicant',[7]),('spouse',[4]),('dependents',[5,0,6])]:
                    for dependent,count in enumerate(counts):
                        visible=[]
                        for row in range(max(3,count)):
                            path=c['groups'][group]['row'].replace('{dependent}',str(dependent)).replace('{row}',str(row))+'.buttons'
                            try:presence=resolve(form,path,saved=True).get('presence')
                            except ValueError:presence=None
                            if presence=='visible':visible.append(row)
                        self.assertEqual(visible,[max(3,count)-1],group)
                runtime=os.environ.get('IMM5562_XFA_RUNTIME')
                if runtime:
                    sys.path.insert(0,runtime)
                    import pypdfium2_raw.bindings as raw
                    raw.FPDF_ERR_XFALOAD=7;raw.FPDF_ERR_XFALAYOUT=8
                    import pypdfium2 as pdfium
                    with pdfium.PdfDocument(out) as document:
                        document.init_forms();document.save(Path(work)/'native.pdf')
                    native=E.fromstring(packets(Path(work)/'native.pdf')['datasets'])
                    for path,value in fields.items():self.assertEqual((resolve(native,path).text or '').strip(),value,path)
                with self.assertRaisesRegex(ValueError,'Unallowlisted'):
                    fill_travels({**payload,'fields':{'IMM_5562.Page2.Item3.Signature':'fake'}},Path(work)/'bad.pdf',blank)
                with self.assertRaisesRegex(ValueError,'export'):
                    fill_travels({**payload,'fields':{c['groups']['applicant']['selector']:'Y'}},Path(work)/'bad.pdf',blank)

    def test_blank_unconfirmed_drafts_stay_downloadable(self):
        from imm5562_pdf import fill_travels
        with tempfile.TemporaryDirectory(dir=BASE/'storage/app/imm/tmp') as work:
            for lang,suffix in [('en','e'),('fr','f')]:
                out=Path(work)/(lang+'.pdf')
                fill_travels({'form':'IMM5562','language':lang,'fields':[],'imm5562_rows':{'applicant':0,'spouse':0,'dependents':[]},'imm5562_state':{'partner':None,'dependents':None}},out,BASE/f'storage/app/imm/imm5562{suffix}.pdf')
                self.assertTrue(out.is_file())
