#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
# robocop-uk-to-json
#
# Copyright (C) 2025 Taler Systems SA
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <https://www.gnu.org/licenses/>.
"""
UK OFSI Consolidated List XML to robocop JSON converter.

Converts the UK OFSI "ConList" (the <ArrayOfFinancialSanctionsTarget> schema,
default namespace http://schemas.hmtreasury.gov.uk/ofsi/consolidatedlist) into
robocop's internal JSON format. The OFSI list is FLAT: each
<FinancialSanctionsTarget> is a single name/alias row, and rows that share a
<GroupID> are the same designated target. This converter groups rows by GroupID
so one robocop record (ssid "GB-<GroupID>") accumulates every name variation,
address and attribute, using the same registry field names as robocop-ch-to-json.

Usage:
    robocop-uk-to-json < ConList.xml | robocop-json-postprocess > uk.json
"""

import xml.etree.ElementTree as ET
import json
import sys
import argparse


def strip_ns(root):
    for el in root.iter():
        if isinstance(el.tag, str) and "}" in el.tag:
            el.tag = el.tag.split("}", 1)[1]
    return root


def text(el, tag):
    child = el.find(tag)
    if child is not None and child.text and child.text.strip():
        return child.text.strip()
    return None


def add(rec, key, value):
    if value is None:
        return
    value = value.strip()
    if not value:
        return
    rec.setdefault(key, []).append(value)


def dedupe(rec):
    for key, val in rec.items():
        if isinstance(val, list):
            seen = set()
            rec[key] = [x for x in val if not (x in seen or seen.add(x))]
    return rec


def date_only(value):
    """OFSI dates look like 2022-12-09T00:00:00; keep the date part."""
    if value and "T" in value:
        return value.split("T", 1)[0]
    return value


def convert(root):
    groups = {}
    order = []
    for t in root.findall("FinancialSanctionsTarget"):
        gid = text(t, "GroupID") or ""
        if gid not in groups:
            type_desc = (text(t, "GroupTypeDescription") or "").lower()
            target_type = "individual" if type_desc == "individual" else (
                "entity" if type_desc == "entity" else "other")
            groups[gid] = {
                "ssid": "GB-{}".format(gid),
                "foreign_identifier": text(t, "UKSanctionsListRef"),
                "target_type": target_type,
                "justification": [],
                "other_information": [],
            }
            order.append(gid)
        rec = groups[gid]
        is_entity = rec["target_type"] == "entity"
        addr_prefix = "REGISTERED_OFFICE_ADDRESS_" if is_entity else "ADDRESS_"

        # Name parts: name1..name5 are forenames, Name6 is the family name.
        forenames = [text(t, "name{}".format(i)) for i in range(1, 6)]
        forenames = [p for p in forenames if p]
        family = text(t, "Name6")
        whole = " ".join(p for p in (forenames + [family]) if p)
        if is_entity:
            add(rec, "COMPANY_NAME", whole)
            add(rec, "BUSINESS_DISPLAY_NAME", whole)
        else:
            for p in forenames:
                add(rec, "PERSON_FIRST_NAMES", p)
            add(rec, "PERSON_LAST_NAME", family)
            add(rec, "FULL_NAME", whole)
        add(rec, "FULL_NAME", text(t, "NameNonLatinScript"))

        gender = text(t, "Individual_Gender")
        if gender and "sex" not in rec:
            rec["sex"] = gender.lower()

        # Address.
        line = ", ".join(p for p in (text(t, "Address1"), text(t, "Address2"),
                                     text(t, "Address3"), text(t, "Address4"),
                                     text(t, "Address5"), text(t, "Address6")) if p)
        add(rec, addr_prefix + "LINES", line)
        add(rec, addr_prefix + "ZIPCODE", text(t, "PostCode"))
        add(rec, addr_prefix + "COUNTRY", text(t, "Country"))

        # Individual attributes.
        add(rec, "DATE_OF_BIRTH", date_only(text(t, "Individual_DateOfBirth")))
        add(rec, "NATIONALITY", text(t, "Individual_Nationality"))
        add(rec, "PERSON_NATIONAL_ID", text(t, "Individual_PassportNumber"))
        add(rec, "PERSON_NATIONAL_ID", text(t, "Individual_NINumber"))
        cob = text(t, "Individual_CountryOfBirth")
        tob = text(t, "Individual_TownOfBirth")
        pob = ", ".join(p for p in (tob, cob) if p)
        if pob:
            add(rec, "other_information", "Place of birth: " + pob)

        # Entity attributes.
        add(rec, "COMMERCIAL_REGISTER_NUMBER", text(t, "Entity_BusinessRegNumber"))

        # Contact details and reasons.
        add(rec, "CONTACT_PHONE", text(t, "PhoneNumber"))
        add(rec, "CONTACT_EMAIL", text(t, "EmailAddress"))
        add(rec, "justification", text(t, "UKStatementOfReasons"))
        add(rec, "other_information", text(t, "OtherInformation"))

    return [dedupe(groups[gid]) for gid in order]


def main():
    parser = argparse.ArgumentParser(
        description="Convert the UK OFSI consolidated list (XML) to robocop JSON")
    parser.add_argument("--input", help="Input XML file (default: stdin)")
    parser.add_argument("--output", "-o", help="Output JSON file (default: stdout)")
    parser.add_argument("--indent", type=int, default=2)
    args = parser.parse_args()

    tree = ET.parse(args.input) if args.input else ET.parse(sys.stdin)
    root = strip_ns(tree.getroot())
    targets = convert(root)

    out = open(args.output, "w", encoding="utf-8") if args.output else sys.stdout
    json.dump(targets, out, indent=args.indent, ensure_ascii=False)
    if args.output:
        out.close()
    print("UK: converted {} targets".format(len(targets)), file=sys.stderr)


if __name__ == "__main__":
    main()
