#!/usr/bin/env python3
"""Take IBANs from a sourcefile and apply them to transactions in any number of transfer files.

The sourcefile is a copy+pasted extract from the Buchhaltungs-Excel-Tabellenblatt.
The transfer files have no formatting requirements, beside the following:
  - They MUST have a 'persona.id' or 'persona.cdedbid' column, containing the (CdEDB)ID of the target.
  - They MAY have a 'IBAN' column, containing overrides to IBANs from the source file.
There will be one output file for each transfer file, containing the transfer file columns,
a column with IBANs and a column 'orig_amount' listing all amounts from the source file.
"""

import csv
import decimal
import pathlib
import sys
from collections.abc import Iterable

import click

from cde_sepa.util import parse_amount, parse_id

FIELDNAMES = (
    "date", "amount", "db_id", "family_name", "given_names", "category", "account", "reference", "account_owner", "IBAN"
)  # fmt: skip


@click.command()
@click.argument("sourcefile", type=click.Path(exists=True, dir_okay=False, path_type=pathlib.Path), required=True)
@click.argument("infiles", type=click.Path(exists=True, path_type=pathlib.Path), nargs=-1)
def main(sourcefile: pathlib.Path, infiles: Iterable[pathlib.Path]) -> None:
    iban_map: dict[int, str] = {}
    payment_map: dict[int, decimal.Decimal] = {}

    line: dict[str, str]

    with sourcefile.open("r", encoding="utf-8-sig") as f:
        for line_nr, line in enumerate(csv.DictReader(f, fieldnames=FIELDNAMES, delimiter=";")):
            if persona_id := parse_id(line["db_id"]):
                if persona_id not in iban_map or not iban_map[persona_id]:
                    iban_map[persona_id] = line["IBAN"]
                elif iban_map[persona_id] != line["IBAN"] and line["IBAN"]:
                    msg = (
                        f"Found multiple IBANs for persona {persona_id} ({line['given_names']} {line['family_name']}):"
                    )
                    print(msg.ljust(80, " ") + f"{iban_map[persona_id]} and {line['IBAN']}")
                amount = parse_amount(line["amount"])
                if not amount:
                    print(f"Couldn't parse amount '{line['amount']}' in line {line_nr + 1}.")
                    continue
                if line["account"] == "intern":
                    msg = f"Internal payment for persona {persona_id} ({line['given_names']} {line['family_name']}):"
                    print(msg.ljust(80, " ") + str(amount))
                else:
                    payment_map[persona_id] = payment_map.get(persona_id, decimal.Decimal(0)) + amount

    if not iban_map:
        raise ValueError("Could not determine any IBANs.")

    infiles = list(infiles)

    for infile in infiles:
        if not (infile.exists() and infile.is_file()):
            print(f"Could not find input file {infile}.")
            continue
        if infile == sourcefile:
            print(f"Skipping source file {sourcefile}.")
            continue
        with infile.open("r", encoding="utf-8-sig") as f:
            output = []
            for line in csv.DictReader(f, delimiter=";"):
                if "persona.id" not in line and "persona.cdedbid" not in line:
                    raise ValueError(f"Transfer file {infile} has no persona id column.")
                if not (persona_id := parse_id(line.get("persona.id", "") or line.get("persona.cdedbid", ""))):
                    continue
                output.append(line)
                line["IBAN"] = line.get("IBAN") or iban_map.get(persona_id, "")
                if not line["IBAN"]:
                    print(f"Could not find IBAN for persona {persona_id}")
                line["orig_amount"] = str(payment_map.get(persona_id, "")).replace(".", ",")
        if not output:
            print(f"Empty input file {infile}.")
            continue
        output_fieldnames = tuple(output[0].keys())
        outfile = infile.parent / (infile.stem + "-out.csv")
        with outfile.open("w", newline="", encoding="utf-8-sig") as f:
            w = csv.DictWriter(f, output_fieldnames, delimiter=";")
            w.writeheader()
            for line in output:
                w.writerow(line)
        print(f"Wrote {len(output)} lines to {outfile}")


if __name__ == "__main__":
    if not len(sys.argv) >= 3:
        print("Please give me at least 2 filenames. First a data source, then any number of transfer lists.")
        sys.exit()

    sourcefile = pathlib.Path(sys.argv[1])

    if not (sourcefile.exists() and sourcefile.is_file()):
        print(f"Could not find source file {sourcefile}")
        sys.exit()

    infiles = list(map(pathlib.Path, sys.argv[2:]))
    main(sourcefile, infiles)
