import csv
import logging
import os
import re
from subprocess import getstatusoutput, getoutput

from scripts.verifyGaussPython.src.python.PropertyFile import get_encoding_format, get_encoding_format_from_shell, \
    read_file
from scripts.verifyGaussPython.src.python.shell_utils import total_dbt_query, failed_dbt_query

totaltypecountmap = dict()
successtypecountmap = dict()
createtable = "CREATE TABLE"
altertable = "ALTER TABLE"
createtype = "CREATE TYPE"
altertype = "ALTER TYPE"
createindex = "CREATE INDEX"
alterindex = "ALTER INDEX"
createsequence = "CREATE SEQUENCE"
altersequence = "ALTER SEQUENCE"
createsession = "CREATE SESSION"
altersession = "ALTER SESSION"
createsynonym = "CREATE SYNONYM"
altersynonym = "ALTER SYNONYM"
createview = "CREATE VIEW"
alterview = "ALTER VIEW"
createfunction = "CREATE FUNCTION"
createprocedure = "CREATE PROCEDURE"
createpackage = "CREATE PACKAGE"
createschema = "CREATE SCHEMA"
anonymousblock = "ANONYMOUS BLOCK"
success_check = None


def get_dict_data(dictmap):
    temp = dict()
    for key in dictmap:
        temp[key] = dictmap[key]
    return temp


def gettotalquerystatus(outputfol, homepath):
    filename = os.path.join(outputfol, 'output')
    dsqlpath = os.path.join(homepath, 'config', 'features-teradata.properties')
    configdata = read_file(dsqlpath)
    dsql = configdata.get('dsqlsupport')
    for dirs_file_name in os.walk(filename):
        for fle in dirs_file_name[2]:
            try:
                flag = False
                encoding = get_encoding_format(homepath)
                fle = os.path.join(dirs_file_name[0], fle)
                if encoding is None:
                    encoding = get_encoding_format_from_shell(fle)
                with open(fle, 'r', encoding=encoding) as fl:
                    lines = fl.readlines()
                    current_map = get_dict_data(totaltypecountmap)
                    update_blogic_count(dsql, lines)
                    if is_equal(current_map, totaltypecountmap) is True:
                        flag = True
                    if flag is True:
                        update_bulk_count(lines)
                fl.close()
            except (UnicodeDecodeError, LookupError, OSError) as error:
                logging.error("Error occurred during data verification %s", error)


def is_equal(dict1, dict2):
    length1 = len(dict1)
    length2 = len(dict2)
    if length1 != length2:
        return False
    for key in dict2:
        if key not in dict1:
            return False
        if dict1[key] != dict2[key]:
            return False
    return True


def update_blogic_count(dsql, lines):
    totalqueryfindpattern = re.compile(
        r'(?i)^(?:create\s+)(?:.*?)(?:package|procedure|function)')
    type_line = str()
    for line in lines:
        query = totalqueryfindpattern.match(line)
        if query:
            matcherstr = query.group().upper().strip()
            if 'FUNCTION' in matcherstr:
                update_total_count_map(createfunction)
            elif 'PROCEDURE' in matcherstr:
                update_total_count_map(createprocedure)
            else:
                update_total_count_map(createpackage)
        else:
            string = line.upper()
            if 'ANONYMOUSBLOCK' in string:
                blockmatch = re.match(r"(?i)^\s*(\/\*~#~AnonymousBlock~#~\*\/)", string)
                if blockmatch:
                    update_total_count_map("ANONYMOUS BLOCK")
            elif 'DECLARE' in string:
                if dsql is not None and dsql.lower() == 'true':
                    update_total_count_map("ANONYMOUS BLOCK")
            else:
                schema_type_pattern = re.compile(r"(?i)^(?:create\s+)(?:schema|type)")
                schema_type = type_line.strip() + string
                query = schema_type_pattern.match(schema_type)
                if query:
                    matcherstr = query.group().upper().strip()
                    if 'SCHEMA' in matcherstr:
                        update_total_count_map(createschema)
                    elif 'TYPE' in matcherstr:
                        update_total_count_map(createtype)
                elif 'ON' in string:
                    grantstr = type_line.strip() + string
                    match = re.match(r"(?i)^(GRANT\s+EXECUTE\s+ON)(.*)", grantstr)
                    if match:
                        update_total_count_map('GRANT')
            type_line = string


def update_bulk_count(lines):
    totalqueryfindpattern = re.compile(
        r'(?i)^\s*(?:CREATE|ALTER)\s+(.*?)?(?<!\w)(TYPE|TABLE|SCHEMA|INDEX|SEQUENCE|SESSION|VIEW|SYNONYM)')
    length = len(lines)
    for index in range(length):
        createaltermatch = re.match("(?i)^create|alter", lines[index].strip())
        query = None
        if createaltermatch:
            if index < length - 1:
                query = totalqueryfindpattern.match(lines[index] + lines[index + 1])
        if query:
            matcherstr = query.group().upper().strip()
            if 'TABLE' in matcherstr:
                update_total_count_map_bulk(createtable, altertable, matcherstr)
            elif 'TYPE' in matcherstr:
                update_total_count_map_bulk(createtype, altertype, matcherstr)
            elif 'INDEX' in matcherstr:
                update_total_count_map_bulk(createindex, alterindex, matcherstr)
            elif 'SEQUENCE' in matcherstr:
                update_total_count_map_bulk(createsequence, altersequence, matcherstr)
            elif 'SESSION' in matcherstr:
                update_total_count_map_bulk(createsession, altersession, matcherstr)
            elif 'SYNONYM' in matcherstr:
                update_total_count_map_bulk(createsynonym, altersynonym, matcherstr)
            else:
                if 'VIEW' in matcherstr:
                    update_total_count_map_bulk(createview, alterview, matcherstr)
                elif 'SCHEMA' in matcherstr:
                    update_total_count_map(createschema)
        else:
            string = lines[index].upper().strip()
            if 'COMMENT' in string:
                if index < length - 1:
                    string = (string + lines[index + 1]).strip().upper()
                    commmentmatch = re.match(r"(?i)^\s*(comment\s+)(.*)", string)
                    if commmentmatch:
                        update_total_count_map("COMMENT")
            elif 'INSERT' in string:
                insertmatch = re.match(r"(?i)^\s*(insert\s*)(.*)", string)
                if insertmatch:
                    update_total_count_map("INSERT")

            elif 'DELETE' in string:
                deletematch = re.match(r"(?i)^\s*(delete\s*)(.*)", string)
                if deletematch:
                    update_total_count_map("DELETE")
            elif 'UPDATE' in string:
                updatematch = re.match(r"(?i)^\s*(update\s*)(.*)", string)
                if updatematch:
                    update_total_count_map("UPDATE")
            elif 'TRUNCATE' in string:
                truncatematch = re.match(r"(?i)^\s*(truncate\s*)(.*)", string)
                if truncatematch:
                    update_total_count_map("TRUNCATE")


def update_total_count_map_bulk(create_object_type, alter_object_type, matcherstr):
    if matcherstr.startswith('CREATE'):
        update_total_count_map(create_object_type)
    else:
        update_total_count_map(alter_object_type)


def update_total_count_map(object_type):
    count = totaltypecountmap.get(object_type)
    if count is None:
        totaltypecountmap[object_type] = 1
    else:
        totaltypecountmap[object_type] = count + 1


def gettypecountstatus(logpath):
    folder = logpath
    for fle in os.listdir(folder):
        if 'gaussexecutionlog' in fle:
            fle = os.path.join(folder, fle)
            gettypecountbulkfile(fle)

    folder = logpath
    for fle in os.listdir(folder):
        if 'gaussexecutionlog' in fle:
            fle = os.path.join(folder, fle)
            gettypecountblogicfile(fle)


def clearlogfile(logpath):
    """Clears existing log file"""
    folder = logpath
    for fle in os.listdir(folder):
        if 'gaussexecution' in fle or 'preverificationexecution' in fle or 'statement' in fle:
            fle = os.path.join(folder, fle)
            cmd = "rm {0} ".format(fle)
            getstatusoutput(cmd)
    output_log_file_name = os.path.join(logpath, 'gaussexecutionlog.log')
    if not os.path.exists(output_log_file_name):
        cmd = "touch {0}".format(output_log_file_name)
        getstatusoutput(cmd)
        cmd = "chmod 600 {0} ".format(output_log_file_name)
        getstatusoutput(cmd)
    output_error_file_name = os.path.join(logpath, 'gaussexecutionerror.log')
    if not os.path.exists(output_error_file_name):
        cmd = "touch {0}".format(output_error_file_name)
        getstatusoutput(cmd)
        cmd = "chmod 600 {0} ".format(output_error_file_name)
        getstatusoutput(cmd)
    csvfilename = os.path.join(logpath, 'statement_result.csv')
    if not os.path.exists(csvfilename):
        cmd = "touch {0}".format(csvfilename)
        getstatusoutput(cmd)
        cmd = "chmod 600 {0} ".format(csvfilename)
        getstatusoutput(cmd)


def gettypecountbulkfile(filename):
    """Count all success data types from log file"""
    if os.path.exists(filename):
        getdatafromfile(createtable, filename)
        getdatafromfile(altertable, filename)

        getdatafromfile(createtype, filename)
        getdatafromfile(altertype, filename)

        getdatafromfile(createindex, filename)
        getdatafromfile(alterindex, filename)

        getdatafromfile(createsequence, filename)
        getdatafromfile(altersequence, filename)

        getdatafromfile(createsession, filename)
        getdatafromfile(altersession, filename)

        getdatafromfile(createsynonym, filename)
        getdatafromfile(altersynonym, filename)

        getdatafromfile(createview, filename)
        getdatafromfile(alterview, filename)

        getdatafromfile(createschema, filename)

        cmd = "grep -w \"COMMENT\\$\" {0}|wc -l".format(filename)
        getdatafromfileshell(cmd, "COMMENT")

        cmd = "grep -E \"^INSERT\\s[0-9]+\\s[0-9]+$\" {0}|wc -l".format(filename)
        getdatafromfileshell(cmd, "INSERT")

        cmd = "grep -E \"^DELETE\\s[0-9]+\" {0}|wc -l".format(filename)
        getdatafromfileshell(cmd, "DELETE")

        cmd = "grep -E \"^UPDATE\\s[0-9]+\" {0}|wc -l".format(filename)
        getdatafromfileshell(cmd, "UPDATE")

        cmd = "grep -E \"^TRUNCATE\\sTABLE\" {0}|wc -l".format(filename)
        getdatafromfileshell(cmd, "TRUNCATE")


def getdatafromfileshell(cmd, datetype):
    """ Get count from log file and update dictionary """
    output = getoutput(cmd)
    value = successtypecountmap.get(datetype)
    if value is None:
        value = 0
    successtypecountmap[datetype] = int(output) + int(value)


def getdatafromfile(datetype, filename):
    """ Get count from log file and update dictionary """
    cmd = "grep -w \"{0}\\$\" {1}|wc -l".format(datetype, filename)
    output = getoutput(cmd)
    value = successtypecountmap.get(datetype)
    if value is None:
        value = 0
    successtypecountmap[datetype] = int(output) + int(value)


def gettypecountblogicfile(filename):
    """Count all success data types from log file"""
    if os.path.exists(filename):
        getdatafromfile(createfunction, filename)

        getdatafromfile(createprocedure, filename)

        getdatafromfile('GRANT', filename)

        getdatafromfile(createpackage, filename)

        cmd = "grep -E \"^ANONYMOUS\\sBLOCK\" {0}|wc -l".format(filename)
        getdatafromfileshell(cmd, anonymousblock)


def writeandprintoutput(logpath):
    """Write verification data to csv file and print in console"""
    header = ['Statement', 'Total', 'Success', 'Failed']
    rows = list()
    filename = os.path.join(logpath, 'statement_result.csv')
    with os.fdopen(os.open(filename, os.O_WRONLY | os.O_CREAT | os.O_EXCL, stat.S_IWUSR | stat.S_IRUSR), 'w') as csvfile:
        try:
            csvwriter = csv.writer(csvfile)
            csvwriter.writerow(header)
            for key, value in totaltypecountmap.items():
                if value != 0:
                    data = successtypecountmap.get(key)
                    if data is not None:
                        if isinstance(data, int):
                            successvalue = int(data)
                        else:
                            successvalue = 0
                        if successvalue > value:
                            successvalue = value
                    else:
                        successvalue = 0
                    rows.append([key, value, successvalue, value - successvalue])
            csvwriter.writerows(rows)
        except OSError as error:
            logging.error("Error occurred during writing to csv file %s", error)
        finally:
            csvfile.close()
    if len(totaltypecountmap) == 0:
        return
    print("\nSummary of Verification : ")
    print("\n======================================================================================================="
          "=====\n")
    print("{0:<25}| {1:<20}| {2:<20}| {3:<20}| {4:<20}\n".format('Statement', 'Total', 'Passed', 'Failed',
                                                                 'Success Rate(%)'))
    print("---------------------------------------------------------------------------------------------------------"
          "---\n")
    totalquery = 0
    totalsuccess = 0
    for key, value in totaltypecountmap.items():
        if value != 0:
            data = successtypecountmap.get(key)
            if data is not None:
                if isinstance(data, int):
                    successvalue = int(data)
                else:
                    successvalue = 0

                if successvalue > value:
                    successvalue = value
            else:
                successvalue = 0
            totalquery = totalquery + value
            totalsuccess = totalsuccess + successvalue
            print("{0:<25}| {1:<20}| {2:<20}| {3:<20}| {4:<20}\n".format(key, value, successvalue, value - successvalue,
                                                                         int((successvalue * 100) / value)))
    print("------------------------------------------------------------------------------------------------------"
          "------\n")
    global success_check
    if totalquery != 0:
        success_check = int((totalsuccess * 100) / totalquery)
        print("{0:<25}| {1:<20}| {2:<20}| {3:<20}| {4:<20}\n".format('Total', totalquery, totalsuccess,
                                                                    totalquery - totalsuccess,
                                                                    success_check))
        print("======================================================================================================="
              "=====\n")
    else:
        print("totalquery is 0")


def writeandprintoutputdbt(logpath):
    """Write verification data to csv file and print in console"""
    totaldbtquery = total_dbt_query()
    faileddbtquery = failed_dbt_query()

    header = ['Statement', 'Total', 'Success', 'Failed']
    rows = list()
    filename = os.path.join(logpath, 'statement_result.csv')
    with os.fdopen(os.open(filename, os.O_WRONLY | os.O_CREAT | os.O_EXCL, stat.S_IWUSR | stat.S_IRUSR), 'w') as csvfile:
        try:
            csvwriter = csv.writer(csvfile)
            csvwriter.writerow(header)
            if faileddbtquery > totaldbtquery:
                faileddbtquery = totaldbtquery
            rows.append(['Total', totaldbtquery, totaldbtquery - faileddbtquery, faileddbtquery])
            csvwriter.writerows(rows)
        except OSError as error:
            logging.error("Error occurred during writing to csv file %s", error)
        finally:
            csvfile.close()
    if totaldbtquery == 0:
        return
    print("\nSummary of Verification : ")
    print("\n======================================================================================================"
          "======\n")
    print("{0:<25}| {1:<20}| {2:<20}| {3:<20}| {4:<20}\n".format('Statement', 'Total', 'Passed', 'Failed',
                                                                 'Success Rate(%)'))
    print("---------------------------------------------------------------------------------------------------------"
          "---\n")
    global success_check
    success_check = int(((totaldbtquery - faileddbtquery) * 100) / totaldbtquery)
    print("{0:<25}| {1:<20}| {2:<20}| {3:<20}| {4:<20}\n".format('Total', totaldbtquery, totaldbtquery - faileddbtquery,
                                                                 faileddbtquery, success_check))
    print("========================================================================================================"
          "====\n")


def print_log_check():
    """ No log path print for 100 % success check"""
    if 100 == success_check:
        return 0
    return 1
