"""
For documentation on this script, run with -h flag
"""
from __future__ import division

import sys
import logging
import os
import time
import pandas as pd

from argparse import ArgumentParser
from argparse import RawTextHelpFormatter
from datetime import datetime as dt


def run_app():
    """Run the logic of application"""
    gdb, logger = get_input_parameter()

    start_time = time.time()
    # input_fc = fc

    logger.info('    Python ' + sys.version)

    # Set the Geoprocessing environment...
    arcpy.OverwriteOutput = 1

    logger.info('    - Setting Up Workspace')

    if not arcpy.Exists(gdb):
        logger.error(' **** User-specified input fgdb does not exist: ' + gdb)
        sys.exit(100)

    check_common_proj_attributes(gdb, logger)

    end_time = time.time()
    execution_time = sanitize_elapsed_time(end_time - start_time)

    logger.info('    Execution Time: ' + execution_time)

def get_input_parameter():
    try:
        # Parse arguments
        parser = ArgumentParser(description="""Check that attributes common to TEI_Long_Tbl, 
                                TEI_Project_Boundaries and/or TEI_Project_Details have the same values.""",
                                formatter_class=RawTextHelpFormatter)
        parser.add_argument('gdb', help='Geodatabase holding TEI_Long_Tbl, TEI_Project_Boundaries and/or TEI_Project_Details')
        parser.add_argument('--log_level', default='INFO', choices=['DEBUG', 'INFO', 'WARNING', 'ERROR'],
                            help='Log level')
        parser.add_argument('--log_dir', help='Path to log Directory')
        args = parser.parse_args()

        log_name = 'main_logger'
        logger = logging.getLogger(log_name)

        log_fmt = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
        log_file_base_name = os.path.basename(sys.argv[0])
        log_file_extension = 'log'
        timestamp = dt.now().strftime('%Y-%m-%d_%H-%M-%S')
        log_file = "{}_{}.{}".format(timestamp, log_file_base_name, log_file_extension)

        logger.setLevel(args.log_level)

        sh = logging.StreamHandler()
        sh.setLevel(args.log_level)
        sh.setFormatter(log_fmt)
        logger.addHandler(sh)

        if args.log_dir:
            try:
                os.makedirs(args.log_dir)
            except OSError:
                pass

            fh = logging.FileHandler(os.path.join(args.log_dir, log_file))
            fh.setLevel(args.log_level)
            fh.setFormatter(log_fmt)
            logger.addHandler(fh)

        return args.gdb, logger

    except Exception as e:
        print('Unexpected exception. Program terminating.')
        exit(1)


def compare_tuples(iterator):
    tuples = list(iterator)
    min_len = min(len(tup) for tup in tuples)
    differing_indices = [i for i in range(min_len) if len(set(tup[i] for tup in tuples)) > 1]
    return differing_indices


def find_unequal_sets(set_dict):
    unequal_sets = []
    fcs = list(set_dict.keys())
    sets = list(set_dict.values())

    for i, (set1, set2) in enumerate(zip(sets, sets[1:]), 1):
        if set1 != set2:
            unequal_sets.append((fcs[i - 1], set1, fcs[i], set2))

    return unequal_sets


def check_common_proj_attributes(gdb, logger):
    arcpy.env.workspace = gdb

    common_fields = ['PROJ_TYPE', 'BAPID', 'PROJ_SCALE', 'PROJ_ID']
    logger.info('    - Verifying common fields {} from TEI_Long_Tbl, TEI_Project_Details, TEI_Project_Boundaries hold common values.'.format(common_fields))

    fcs_to_check = [fc for fc in arcpy.ListFeatureClasses() if fc in ['TEI_Long_Tbl', 'TEI_Project_Boundaries'] and int(arcpy.GetCount_management(fc).getOutput(0)) > 0]
    if arcpy.Exists('TEI_Project_Details') and int(arcpy.GetCount_management('TEI_Project_Details').getOutput(0)) > 0:
        fcs_to_check.append('TEI_Project_Details')
    
    if not fcs_to_check:
        logger.error('        - Feature classes either missing or empty. Fix and re-run.')
        sys.exit(100)
    
    elif not any(i in ['TEI_Project_Details', 'TEI_Project_Boundaries'] for i in fcs_to_check) or len(fcs_to_check) < 2:
        logger.error('        - Did not find enough non-empty feature classes to compare. {} only has {}. You must submit a TEI_Long_Tbl and project level data in TEI_Project_Details or TEI_Project_Boundaries. Fix and re-run.'.format(gdb, fcs_to_check))
        sys.exit(100)

    elif len(fcs_to_check) >= 2:
        logger.info('        - Found non-empty feature classes: {}. Checking common fields.'.format(fcs_to_check))

        compare_dict = {}
        for fc in fcs_to_check:
            unique_rows = set(tuple(row) for row in arcpy.da.SearchCursor(fc, common_fields))
            if len(unique_rows) > 1:
                differing_indices = compare_tuples(unique_rows)
                differing_fields = [common_fields[i] for i in differing_indices]
                logger.warning('** WARNING Differing values for {} within {}. Confirm this is acceptable for this particular project.'.format(differing_fields, fc))
            
            compare_dict[fc] = unique_rows
        
        if all(set == compare_dict.values()[0] for set in compare_dict.values()):
            logger.info('        - Success! Common fields from TEI_Long_Tbl, TEI_Project_Details, TEI_Project_Boundaries hold common values.')
        else:
            unequal = find_unequal_sets(compare_dict)
            for name1, set1, name2, set2 in unequal:
                differing_indices = compare_tuples(set1.union(set2))
                differing_fields = [common_fields[i] for i in differing_indices]
                logger.warning('** WARNING Common fields do NOT hold common values. {} is not equal to {} for field(s) {}. Fix and re-run.'.format(name1, name2, differing_fields))


def stra(value):
    """
    Converts any value to a string. Replaces Python's stra() which incurs an error if the value
    happens to have a unicode character in it. Unicode characters will be replaced with ?

    :param any value: input value
    :return: value as string
    :rtype: str
    """
    if sys.version_info[0] < 3:
        return unicode(value).encode('ascii', 'replace')
    else:
        return str(value)


def sanitize_elapsed_time(seconds):
    """
    Routine to convert a raw number of seconds into a human-friendly statement of duration

    :param seconds: number of seconds
    :return: formatted duration
    :rtype: str
    """

    if seconds < 120:
        str_elapsed_time = stra(int(seconds)) + ' seconds.'
    elif seconds < 3600:
        str_elapsed_time = stra(int(seconds)) + ' seconds (' + stra(round(seconds / 60, 1)) + ' minutes).'
    else:
        str_elapsed_time = stra(int(seconds)) + ' seconds (' + stra(round(seconds / 3600, 2)) + ' hours).'

    return str_elapsed_time


if __name__ == '__main__':
    try:
        # Import arcpy
        import arcpy
    except:
        logging.error('No ArcGIS licenses available to run this tool.  Program terminating.')
        sys.exit(1)
    run_app()
else:
    try:
        # Import arcpy
        import arcpy
    except:
        logging.error('No ArcGIS licenses available to run this tool.  Program terminating.')
        sys.exit(1)
