#!/usr/bin/env python3.7

import codecs
import csv
import logging.config
import os
import shutil
import time

from typing import Dict, List, Tuple

from common import get_column_index_csv, get_data_root ,setup_logging

DATA_FOLDER_NAME="Matching_Parent_EAN"


def read_refs(ref_path: str)->Tuple[dict, list]:
	""" Read any reference files in the ref folder in lexicographical order. """

	header = list() # type: List[str]
	ref_dict = dict()
	gen_dict = dict()
	ref_files = os.listdir(ref_path)
	article_idx = -1

	if len(ref_files) > 0:
		# Ref files must be ordered. Each file should be named so that sorting
		# puts them in the right order, e.g. ref1.csv, ref2.csv, etc.
		#
		# Note: May require leading zeros if more than 10 files and numbers are
		#			  used since 1ref.csv and 10ref.csv would be read before 2ref.csv
		#				otherwise.
		gen = 1
		for ref_file in sorted(ref_files):
			header_read = False
			full_ref_path = os.path.join(ref_path, ref_file)
			with codecs.open(full_ref_path, "r", encoding='utf-8-sig') as f:
				reader = csv.reader(f, delimiter=';')

				for row in reader:
						if header_read:
								art_id = row[article_idx].strip()

								if art_id in ref_dict:
									if gen > gen_dict[art_id]:
										ref_dict[art_id] = []
										gen_dict[art_id] = gen

									ref_dict[art_id].append(row[1:])
								else:
									ref_dict[art_id] = [row[1:]]
									gen_dict[art_id] = gen
						else:
							article_idx = get_column_index_csv(row, "article")
							if article_idx < 0:
								raise Exception(f"Article column not found in '{ref_file}'.")

							row.pop(article_idx)
							header = row
							header_read = True
			gen+=1

	return ref_dict, header


def match_files(source_file: str, matched_file: str, unmatched_file: str,
	ref_dict: dict, headers: list)->None:
	""" Matches article IDs from the source file against the reference dictionary
			and copies the ref columns into the output file. Otherwise copy the
			article ID to an unmatched output file. """
	header_read = False

	with codecs.open(source_file, "r", encoding='utf-8') as f, \
		codecs.open(matched_file, "w", encoding='utf-8') as mf, \
		codecs.open(unmatched_file, "w", encoding='utf-8') as uf:

		reader = csv.reader(f, delimiter=';')
		writer_matched = csv.writer(mf, delimiter=';', quoting=csv.QUOTE_NONNUMERIC)
		writer_unmatched = csv.writer(uf, delimiter=';',
			quoting=csv.QUOTE_NONNUMERIC)

		for row in reader:
			if header_read:
				key = row[0]

				if key in ref_dict:
					ref_rows = ref_dict[key]
					for ref_row in ref_rows:
						writer_matched.writerow(row + ref_row)
				else:
					writer_unmatched.writerow(row)
			else:
				article_idx = get_column_index_csv(row, "article_number")
				if article_idx < 0:
					raise Exception(f"Missing Article column in file '{source_file}'")

				writer_matched.writerow(row + headers)
				writer_unmatched.writerow(row)

				header_read = True


def main()->None:
	data_folder = os.path.join(get_data_root(), DATA_FOLDER_NAME)

	path_in = os.path.join(data_folder, "in")
	path_out = os.path.join(data_folder, "out")
	path_prc = os.path.join(data_folder, "processed")
	path_err = os.path.join(data_folder, "error")
	path_log = os.path.join(data_folder, "log")
	path_ref = os.path.join(data_folder, "ref")

	setup_logging(path_log)

	files = os.listdir(path_in)
	num_files = len(files)

	logging.debug(f"Found {num_files} files...")

	if num_files == 0:
			return

	logging.debug("Reading Refs...")
	ref_values, header = read_refs(path_ref)

	if len(ref_values) == 0:
		logging.error("No ref data found. Aborting...")

	logging.debug(f"Read {len(ref_values)} ref entries...")

	for f in files:
			in_file = os.path.join(path_in, f)
			ts = "{}".format(time.strftime("%Y%m%d-%H%M%S"))
			out_matched_file = os.path.join(path_out, f"matched_{ts}.csv")
			out_unmatched_file = os.path.join(path_out, f"errors_{ts}.csv")
			prc_file = os.path.join(path_prc, f)
			err_file = os.path.join(path_err, f)

			if not os.path.isfile(in_file):
				continue

			logging.debug(f"Matching '{in_file}'...")

			try:
					match_files(in_file, out_matched_file, out_unmatched_file,
						ref_values, header)

					if os.path.isfile(prc_file):
							os.remove(prc_file)

					shutil.move(in_file, prc_file)
			except Exception as e:
					logging.exception(e)
					try:
							shutil.move(in_file, err_file)
					except Exception as e:
							logging.exception(e)

	print("Done.")

if __name__ == "__main__":
	main()
