"""IMM1295 contact merge regression checks; Adobe rendering stays manual."""
import json
from pathlib import Path
import re
import unittest
from uuid import uuid4

import pikepdf
from lxml import etree as E
from prefill_imm_xfa_pdf import fill, find_by_path

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


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


def named(root, path):
    node = root
    for part in path.split('.'):
        node = next(child for child in node if child.get('name') == part)
    return node


class Imm1295PhoneStateTest(unittest.TestCase):
    def test_same_address_selection_and_disabled_fields_survive_pdf_save(self):
        selection = 'ContactInformation.contact.SameAsMailingIndicator'
        for language, suffix in [('en', 'e'), ('fr', 'f')]:
            country = 'ResidentialAddressRow2.' + ('Pays.Pays' if language == 'fr' else 'Country.Country')
            blank = BASE / f'storage/app/imm/imm1295{suffix}.pdf'
            original = packets(blank)
            for choice in ['Y', 'N', None]:
                fields = {country: '201'}
                if choice:
                    fields[selection] = choice
                before = dict(fields)
                payload = self.work / 'address.json'
                output = self.work / 'address.pdf'
                payload.write_text(json.dumps({'form': 'IMM1295', 'language': language, 'fields': fields}), encoding='utf-8')
                fill(str(payload), str(output), str(blank))
                saved = packets(output)
                data = E.fromstring(saved['datasets'])
                if choice:
                    group = find_by_path(data, selection)[0]
                    self.assertEqual(choice, group.text)
                    self.assertEqual('dataValue', group.get('{http://www.xfa.org/schema/xfa-data/1.0/}dataNode'))
                    for packet in ['template', 'form']:
                        root = E.fromstring(saved[packet])
                        selected = named(root, 'form1.Page2.' + selection)
                        self.assertEqual([choice], selected.xpath('./*[local-name()="value"]/*/text()'))
                        if choice == 'Y':
                            field = named(root, 'form1.Page2.ContactInformation.contact.' + country)
                            self.assertEqual('readOnly', field.get('access'))
                            self.assertEqual(['disabled'], field.xpath('./*[local-name()="validate"]/@nullTest'))
                if choice == 'Y':
                    self.assertFalse(find_by_path(data, country)[0].text)
                else:
                    self.assertEqual('201', find_by_path(data, country)[0].text)
                for packet in ['template', 'form']:
                    self.assertEqual(E.fromstring(original[packet]).xpath('//*[local-name()="script"]/text()'),
                                     E.fromstring(saved[packet]).xpath('//*[local-name()="script"]/text()'))
                    self.assertEqual(re.findall(br'<\?.*?\?>', original[packet], re.S), re.findall(br'<\?.*?\?>', saved[packet], re.S))
                self.assertEqual(before, fields)

    def setUp(self):
        self.work = BASE / 'storage/app/imm/tmp' / ('1295-phone-' + uuid4().hex)
        self.work.mkdir(parents=True)

    def test_mapper_payload_with_only_selected_checkbox_sets_phone_visibility(self):
        for language, suffix in [('en', 'e'), ('fr', 'f')]:
            for selected in ['CanadaUS', 'Other']:
                fields = {}
                for group in ['Phone', 'AltPhone']:
                    prefix = 'PhoneNumbers.' + group + '.'
                    fields[prefix + selected] = '1'
                    fields[prefix + 'NumberCountry'] = '1' if selected == 'CanadaUS' else '94'
                    if selected == 'CanadaUS':
                        fields.update({prefix + 'NANumber.AreaCode': '416', prefix + 'NANumber.FirstThree': '555', prefix + 'NANumber.LastFive': '0100'})
                    else:
                        fields[prefix + 'IntlNumber.IntlNumber'] = '771234567'
                payload = self.work / 'selected-only.json'
                output = self.work / f'selected-only-{language}-{selected}.pdf'
                payload.write_text(json.dumps({'form': 'IMM1295', 'language': language, 'fields': fields}), encoding='utf-8')
                fill(str(payload), str(output), str(BASE / f'storage/app/imm/imm1295{suffix}.pdf'))
                for packet, content in packets(output).items():
                    if packet not in ['template', 'form']:
                        continue
                    root = E.fromstring(content)
                    for group in ['Phone', 'AltPhone']:
                        prefix = f'form1.Page2.ContactInformation.contact.PhoneNumbers.{group}.'
                        self.assertEqual(named(root, prefix + 'NANumber').get('presence'), 'visible' if selected == 'CanadaUS' else 'invisible')
                        self.assertEqual(named(root, prefix + 'IntlNumber').get('presence'), 'invisible' if selected == 'CanadaUS' else 'visible')

    def test_byte_patch_only_changes_confirmed_phone_branch_presence(self):
        from imm1295_pdf_state import contact_phone_state
        raw = (b'<template xmlns="urn:test"><?keep exact?><subform name="form1">'
               b'<subform name="Page2"><subform name="ContactInformation"><subform name="contact"><subform name="PhoneNumbers"><subform name="Phone">'
               b'<subform name="NANumber" presence="invisible"/>'
               b'<subform name="IntlNumber"/><script>value &lt; 2;</script>'
               b'</subform><subform name="AltPhone"><subform name="NANumber" presence="invisible"/>'
               b'</subform></subform></subform></subform></subform></subform></template>')
        fields = {'PhoneNumbers.Phone.CanadaUS': '1', 'PhoneNumbers.Phone.Other': '0'}
        expected = raw.replace(b'name="NANumber" presence="invisible"', b'name="NANumber" presence="visible"', 1)
        expected = expected.replace(b'name="IntlNumber"/>', b'name="IntlNumber" presence="invisible"/>', 1)
        self.assertEqual(contact_phone_state(raw, fields), expected)
        self.assertEqual(contact_phone_state(raw, {'PhoneNumbers.Phone.CanadaUS': '1'}), expected)
        for fields in ({}, {'PhoneNumbers.Phone.ActualNumber': '+94771234567'},
                       {'PhoneNumbers.Phone.CanadaUS': '1', 'PhoneNumbers.Phone.Other': '1'},
                       {'PhoneNumbers.Phone.CanadaUS': '0', 'PhoneNumbers.Phone.Other': '0'}):
            self.assertEqual(contact_phone_state(raw, fields), raw)

    def test_bilingual_phone_branches_follow_confirmed_exports(self):
        for language, suffix in [('en', 'e'), ('fr', 'f')]:
            blank = BASE / f'storage/app/imm/imm1295{suffix}.pdf'
            original = packets(blank)
            for canada in (True, False):
                fields = {}
                for group in ('Phone', 'AltPhone'):
                    prefix = 'PhoneNumbers.' + group + '.'
                    fields.update({prefix + 'CanadaUS': '1' if canada else '0',
                                   prefix + 'Other': '0' if canada else '1',
                                   prefix + 'NumberCountry': '1' if canada else '94',
                                   prefix + 'ActualNumber': '4165550100' if canada else '+94771234567'})
                    if canada:
                        fields.update({prefix + 'NANumber.AreaCode': '416', prefix + 'NANumber.FirstThree': '555', prefix + 'NANumber.LastFive': '0100'})
                    else:
                        fields[prefix + 'IntlNumber.IntlNumber'] = '771234567'
                snapshot = dict(fields)
                payload = self.work / 'payload.json'
                output = self.work / f'{language}-{canada}.pdf'
                payload.write_text(json.dumps({'form': 'IMM1295', 'language': language, 'fields': fields}), encoding='utf-8')
                fill(str(payload), str(output), str(blank))
                written = packets(output)
                for packet in ('template', 'form'):
                    root = E.fromstring(written[packet])
                    for group in ('Phone', 'AltPhone'):
                        prefix = 'form1.Page2.ContactInformation.contact.' + 'PhoneNumbers.' + group + '.'
                        self.assertEqual(named(root, prefix + 'NANumber').get('presence'), 'visible' if canada else 'invisible')
                        self.assertEqual(named(root, prefix + 'IntlNumber').get('presence'), 'invisible' if canada else 'visible')
                        if packet == 'template':
                            checkbox = named(root, prefix + 'CanadaUS')
                            exports = checkbox.xpath('./*[local-name()="items"]/*')
                            self.assertEqual([(E.QName(node).localname, node.text) for node in exports],
                                             [('integer', '1'), ('integer', '0'), ('integer', '2')])
                    before = E.fromstring(original[packet])
                    self.assertEqual(before.xpath('//*[local-name()="script"]/text()'), root.xpath('//*[local-name()="script"]/text()'))
                    self.assertEqual(re.findall(br'<\?.*?\?>', original[packet], re.S), re.findall(br'<\?.*?\?>', written[packet], re.S))
                for packet in original:
                    if packet not in ('template', 'form', 'datasets'):
                        self.assertEqual(original[packet], written[packet], packet)
                data = E.fromstring(written['datasets'])
                for path, value in fields.items():
                    self.assertEqual(find_by_path(data, path)[0].text, value, path)
                self.assertEqual(fields, snapshot)
                self.assertEqual(original, packets(blank))

if __name__ == '__main__':
    unittest.main()
