import re
import csv
import argparse
import sys
from itertools import groupby
from operator import itemgetter

cql_to_sql_reg_pairs = ( \
        (r".*CQL query:\s+(.+)", ( r".*SQL generated from CQL:\s+(.*)",) ),
        (r".*CqlHelper\s+Encoding query\s+(.*)", (r".*SQL generated by CQL query\s+.+:\s+(.*)", r".*SQL generated from CQL:\s+(.*)"))
)

#regex for new unified format
unified_sql_cql_reg = r".*CQL >>> SQL:\s+(.+)\s>>>\s+(.+)"

def get_time(log_handle, max_lines=3):
    time_reg = r".*ProxyContext.*\d+\s+(\d+)us\s+.*"
    count = 1
    line = log_handle.readline()
    while line and count <= max_lines:
        time_match = re.match(time_reg, line)
        if time_match:
            return time_match.group(1)
        line = log_handle.readline()
    return None

def get_sql(sql_reg_list, log_handle, max_lines=4):
    """
    sql_reg_list: A list of regular expressions to attempt to match a line of log containing SQL
    log_handle: An open handle to a log file that we're searching
    max_lines: The maximum number of lines to try searching before bailing
    """
    count = 1
    line = log_handle.readline()
    while line and count <= max_lines:
        sql_match = None
        for sql_reg in sql_reg_list:
            sql_match = re.match(sql_reg, line)
            if sql_match:
                return sql_match.group(1)
        count = count + 1
        line = log_handle.readline()
    return None

def get_dedup_list(original_seq):
    original_list = list(original_seq)
    original_list.sort(key = itemgetter(1))
    groups = groupby(original_list, itemgetter(1))
    grouped_list = [[item for item in data] for (key, data) in groups]
    new_list = []
    for entry in grouped_list:
        new_list.append(max(entry, key=lambda x: x[0]))
    return new_list


def get_queries_from_logfile(log_handle, max_lines=4, dedup=False, debug=False):
    log_reg = r"(?P<date>\d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d,\d+)\s+(?P<level>\S+)\s+(?P<class>\S+)\s+(?P<module>\S+)\s(?P<body>.*)" #Match the major pieces of a log entry
    result_list = []
    line = log_handle.readline()
    while line:
         #try the unified reg first
        unified_match = re.match(unified_sql_cql_reg, line)
        if unified_match:
            time = get_time(log_handle)
            if time:
                result_list.append( (time, unified_match.group(1), unified_match.group(2)) )
                line = log_handle.readline() #force next read
                if debug:
                    print("Got match from unified reg")
                continue
        line_match = re.match(log_reg, line)
        if not line_match:
            line = log_handle.readline()
            continue
        for pair in cql_to_sql_reg_pairs:
            cql_reg = pair[0]
            cql_match = re.match(cql_reg, line_match.group('body')) #try to see if the logline matches a CQL pattern
            if cql_match:
                sql = get_sql(pair[1], log_handle, max_lines)
                if sql:
                    time = get_time(log_handle)
                    if time:
                        result_list.append( (time, cql_match.group(1), sql) )
                        if debug:
                            print("Got match from legacy reg")
        line = log_handle.readline()
    if dedup:
        result_list = get_dedup_list(result_list)
    return result_list

def get_query_csv(log_file_path, csv_file_path, max_lines=4, dedup=False, debug=False):
    with open(log_file_path, 'r') as log_handle:
        result_list = get_queries_from_logfile(log_handle, max_lines, dedup, debug)
        with open(csv_file_path, 'w', newline='') as csv_handle:
            csv_writer = csv.writer(csv_handle)
            for entry in result_list:
                csv_writer.writerow(entry)


if __name__ == "__main__":
    parser = argparse.ArgumentParser(description='Parse out cql and sql information from an Okapi logfile')
    parser.add_argument('--max-sql-scan-lines', type=int, action='store',\
            default=4, help="The number of lines to scan for SQL following finding CQL")
    parser.add_argument('--debug', type=bool, action='store', default=False, help="Debug mode")
    parser.add_argument('--dedup', type=bool, action='store', default=False, help="Remove duplicate CQL entries")
    parser.add_argument('logfile', help="The path to the Okapi logfile")
    parser.add_argument('csvfile', help="A path to write the csv output to")

    args = parser.parse_args()
    try:
        get_query_csv(args.logfile, args.csvfile, args.max_sql_scan_lines, args.dedup, args.debug)
    except Exception as e:
        if(args.debug):
            raise e
        sys.stderr.write(f"Error processing logs: {e}\n")
        sys.exit(1)




