#!/usr/bin/env python3
""" Process Nike Anlage """

import codecs
import csv
import os
import logging
import shutil
import time
from typing import Dict, List, Any, Set

from common import setup_logging, get_data_root, get_column_index_csv, parse_money

DATA_FOLDER_NAME = "Matching_Nike_Anlage"

COLS_IN = [
    "UPC Code", "Artikel / Farb Nummer", "Artikelnummer", "Farbbeschreibung",
    "Farbnummer", "Size ID", "Artikelname", "Category Beschreibung",
    "Liefereinheit", "Preis - Einkauf", "Preis - UVP"
]

COLS_OUT = [
    "Artikelnummer", "CAT_NAME", "MODEL_NR", "BRAND_NR", "Description",
    "LongDescription", "fedas", "VAT", "SUP_COLOR_NR", "IDE_COLOR_CODE",
    "SUP_COLOR_NAME", "Picture", "EAN", "IDE_SIZE_NAME", "IDESizeIndex",
    "SUP_SIZE_NAME", "PKG_UNIT", "PURCHASE_PRICE", "RETAIL_PRICE", "Preis_2",
    "Preis_3", "NOS_FLAG", "NET_FLAG", "Hersteller", "FPB", "Gewicht",
    "Versandkosten", "Brand", "Variantennummer"
]


def build_column_map(row: list, filename: str) -> dict:
    """ Map expected header column names to column number. """
    col_map = dict()
    for col_name in COLS_IN:
        col_id = get_column_index_csv(row, col_name)
        if col_id < 0:
            raise Exception(f"{col_name} column not found in '{filename}'.")
        col_map[col_name] = col_id

    return col_map


# pylint: disable=too-many-locals, too-many-statements
def read_in_file(file_in: str, data: list, price_data: dict, eans: set):
    """ Read input file """
    header_read = False
    col_idx: Dict[str, int] = dict()  # hint: "col_idx: Dict[str, list]

    # pylint: disable=invalid-name
    with codecs.open(file_in, "r", encoding='utf-16') as f:
        reader = csv.reader(f, delimiter=',')

        for row in reader:
            if header_read:
                article = row[col_idx["Artikelnummer"]]
                color_num = row[col_idx["Farbnummer"]]
                art_col = row[col_idx["Artikel / Farb Nummer"]]
                purch_price = parse_money(row[col_idx["Preis - Einkauf"]])
                new_art_num = f"NI{article}_{color_num}"
                ean = row[col_idx["UPC Code"]]

                if ean in price_data and purch_price > price_data[ean]:
                    # Keep a high water mark for the purchase price to update
                    # all data later
                    price_data[ean] = purch_price
                else:
                    price_data[ean] = purch_price

                if ean in eans:
                    # Only add each ean once
                    continue
                else:
                    eans.add(ean)

                color_lower = row[col_idx["Farbbeschreibung"]].lower()
                descr = "Nike {} - {}".format(row[col_idx["Artikelname"]],
                                              color_lower)
                pic_name = f"{art_col}.jpg"
                size = row[col_idx["Size ID"]]
                variant = f"{new_art_num}#{size}"

                data.append([
                    new_art_num,  # A - Artikelnummer
                    row[col_idx["Category Beschreibung"]],  # B - "CAT_NAME"
                    article,  # C - "MODEL_NR"
                    156,  # D - "BRAND_NR"
                    descr,  # E - "Description"
                    descr,  # F - "LongDescription"
                    "temp_ct",  # G - "fedas"
                    19,  # H - "VAT"
                    color_num,  # I - SUP_new_art_numCOLOR_NR
                    color_num,  # J - IDE_COLOR_CODE
                    color_lower,  # K - SUP_COLOR_NAME
                    pic_name,  # L - Picture
                    row[col_idx["UPC Code"]],  # M - EAN
                    size,  # N - "IDE_SIZE_NAME"
                    None,  # O - "IDESizeIndex"
                    size,  # P - "SUP_SIZE_NAME"
                    row[col_idx["Liefereinheit"]],  # Q - "PKG_UNIT"
                    purch_price,  # R - PURCHASE_PRICE
                    row[col_idx["Preis - UVP"]],  # S - RETAIL_PRICE
                    row[col_idx["Preis - UVP"]],  # T - Preis_2
                    row[col_idx["Preis - UVP"]],  # U - Preis_3
                    None,  # V - NOS_FLAG
                    None,  # W - NET_FLAG
                    "Nike",  # X - Hersteller
                    2,  # Y - FPB
                    299,  # Z - Gewicht
                    None,  # AA - Versandkosten
                    "Nike",  # AB - Brand
                    variant  # AC - Variantennummer
                ])
            else:
                col_idx = build_column_map(row, file_in)
                header_read = True


def write_csv(output_path: str, data: List[Any], header=None, delimiter=';'):
    """ Write data to file in CSV format. """
    logging.info("Writing %s with %d rows.", output_path, len(data))

    # pylint: disable=invalid-name
    with codecs.open(output_path, "w", encoding='utf-8') as of:
        writer = csv.writer(of, delimiter=delimiter, quoting=csv.QUOTE_MINIMAL)

        if header:
            writer.writerow(header)

        for r in data:
            writer.writerow(r)


def main() -> None:
    """ App entry point """
    data_folder = os.path.join(get_data_root(), DATA_FOLDER_NAME)

    path_in = os.path.join(data_folder, "in")
    # Write output directly into the Plenty ID Barcode IN folder
    path_out = os.path.join(get_data_root(), "Matching_Plenty_ID_Barcode",
                            "in")
    path_prc = os.path.join(data_folder, "processed")
    path_err = os.path.join(data_folder, "error")
    path_log = os.path.join(data_folder, "log")

    setup_logging(path_log)

    files = os.listdir(path_in)
    num_files: int = len(files)

    logging.debug("Found %d files...", num_files)

    if num_files == 0:
        return

    data: List[Any] = list()
    price_dict: Dict[str, float] = dict()
    ean_set: Set[str] = set()
    for f in files:  # pylint: disable=invalid-name
        in_file = os.path.join(path_in, f)
        prc_file = os.path.join(path_prc, f)
        err_file = os.path.join(path_err, f)

        if not os.path.isfile(in_file):
            continue

        try:
            read_in_file(in_file, data, price_dict, ean_set)
            if os.path.isfile(prc_file):
                os.remove(prc_file)

            shutil.move(in_file, prc_file)
        except Exception as e:  # pylint: disable=broad-except,invalid-name
            logging.exception(e)
            try:
                shutil.move(in_file, err_file)
            except Exception as e:  # pylint: disable=broad-except,invalid-name
                logging.exception(e)

    if data:
        out_file = os.path.join(
            path_out, "Nike_Net_{}.csv".format(time.strftime("%Y%m%d-%H%M%S")))
        # Update all rows with the highest price found
        for row in data:
            row[17] = price_dict[row[12]]
        data.sort(key=lambda x: x[0])
        write_csv(out_file, data, COLS_OUT)

    logging.info("Done.")


if __name__ == "__main__":
    main()
