"""Vertica SQL Serializer"""
from __future__ import print_function

from collections import OrderedDict
from datetime import datetime
import json
import sys
import re

# Supported SQL flavours
def get_flavors():
    return ["athena","mssql", "mysql","postgres","vertica"]
#
# RECORD LAYOUTS
#
def generateRecordLayout(sql_flavor, workspace, file=sys.stdout, options=[]):
    for resource in workspace.getResources(type="record"):
        generateRecordLayout(sql_flavor, resource, workspace, file=file, options=options)

def generateRecordLayout(sql_flavor, record, workspace, file=sys.stdout, options=[]):
    """Generates SQL"""
    
    if sql_flavor not in get_flavors():
        print(f"Unsupported SQL flavor '{sql_flavor}'")
        return
    # Init
    id = record.getId()
    
    # Generate
    print(f"{sql_flavor}: Generating Record {record.getReference()}")

    # Init
    bank = record.getBank()
    id = record.getId()

    # Validate
    validated = True
    if bank is None:
        print(record.source, "Missing bank")
        validated = False
    if id is None:
        print(record.source, "Missing id")
        validated = False

    # Generate
    if validated:
        print(f"{sql_flavor}: Generating table for "+record.getReference())
        print(file=file)
        print("--",record.getReference(),  file=file)
        print("--",f"Generated by metasheet on {datetime.now().strftime('%Y-%m-%dT%H:%M:%S')}", file=file)

        # CREATE TABLE
        quote_char = get_quote_char(sql_flavor)
        print("# ",record.getReference())
        if sql_flavor == "athena":
            create_stmt = f"create external table if not exists"
        elif sql_flavor == "mssql":
            create_stmt = f"create table "
        else:
            create_stmt = f"create table if not exists"
        print(f"{create_stmt} {quote_char}{id}{quote_char}(", file=file, sep='')
        # Process matching layout entries
        layouts = workspace.getResources(type="layout", bank=bank+"."+id)
        layouts_count = len(layouts)
        for index, layout in enumerate(layouts):
            # variables / fields
            varbank = layout.getPropertyValue("varbank")
            variable = layout.getPropertyValue("id")
            varRef = variable
            if varbank:
                varRef = varbank+"."+varRef
            # Validate
            validated = True
            # Generate
            if validated:
                sql_name = layout.getFacetedPropertyValue(f"name[{sql_flavor}]")
                if layout.hasProperty(f"datatype[{sql_flavor}]"):
                    sql_datatype = layout.getFacetedPropertyValue(f"datatype[{sql_flavor}]")
                else:
                    sql_datatype = infer_datatype(sql_flavor, layout)
                sql = f"{quote_char}{sql_name}{quote_char} {sql_datatype}"
                if index != layouts_count-1:
                    sql += ","
                print(sql,file=file)
        print(")", file=file) # end create table
        if sql_flavor == "athena":
            print("ROW FORMAT SERDE 'org.apache.hadoop.hive.serde2.lazy.LazySimpleSerDe'", file=file) 
            print("WITH SERDEPROPERTIES ('serialization.format' = ',','field.delim' = ',')", file=file) 
            print("LOCATION 's3://bucket/prefix/'", file=file) 
            print("TBLPROPERTIES ('has_encrypted_data'='false','skip.header.line.count'='1');", file=file)
        print(";", file=file) # end command
        # Add CSV import script (commented out)
        if sql_flavor == "postgres":
            print("", file=file) 
            print("-- CSV IMPORT", file=file) 
            print(f"-- COPY {quote_char}{id}{quote_char} from 'path-to-csv-file' DELIMITER ',' CSV HEADER ENCODING 'LATIN1';", file=file) 
            print("", file=file) 
            return infer_mysql_datatype(layout)


def infer_datatype(sql_flavor, layout):
    if sql_flavor == "athena":
        return infer_athena_datatype(layout)
    elif sql_flavor == "mssql":
        return infer_mssql_datatype(layout)
    elif sql_flavor == "mysql":
        return infer_mysql_datatype(layout)
    elif sql_flavor == "postgres":
        return infer_postgres_datatype(layout)
    elif sql_flavor == "vertica":
        return infer_vertica_datatype(layout)

def infer_athena_datatype(layout):
    datatype = layout.getFacetedPropertyValue("datatype").lower()
    width = layout.getFacetedPropertyValue("width")
    sqltype = None
    if datatype == "text":
        sqltype = "string"
    elif datatype == "string":
        if width is None:
            width = 256
        sqltype = "varchar("+str(width)+")"
    elif datatype.startswith("int"):
        sqltype = "int"
    elif datatype.startswith("num") or datatype.startswith("decimal"):
        if width is None:
            sqltype = "double"
        else:
            decimals = layout.getFacetedPropertyValue("decimals")
            if decimals is None:
                decimals = "0"
            if int(decimals) == 0: 
                if int(width) <= 2:
                    sqltype = "tinyint"
                if int(width) <= 4:
                    sqltype = "smallint"
                elif int(width) <= 9:
                    sqltype = "int"
                elif int(width) <= 18:
                    sqltype = "bigint"
                else: 
                    sqltype = "decimal("+str(width)+","+str(decimals)+")"       
            sqltype = "decimal("+str(width)+","+str(decimals)+")"
    elif datatype == "date":
        sqltype = "date"
    elif datatype.startswith("bool"):
        sqltype = "boolean"
    return sqltype

def infer_mssql_datatype(layout):
    datatype = layout.getFacetedPropertyValue("datatype").lower()
    width = layout.getFacetedPropertyValue("width")
    if width:
        width = int(width)
    sqltype = None
    if datatype == "text":
        sqltype = "text"
    elif datatype == "string":
        if width is None:
            width = 256
        sqltype = "varchar("+str(width)+")"
    elif datatype.startswith("int"):
        if width:
            if width <= 2:
                sqltype = "tinyint"
            elif width <= 4:
                sqltype = "smallint"
            elif width <= 6:
                sqltype = "mediumint"
            elif width <= 9:
                sqltype = "int"
            else:
                sqltype = "bigint"
        else:
            sqltype = "int"
    elif datatype.startswith("num") or datatype.startswith("decimal"):
        if width is None:
            sqltype = "real"
        else:
            decimals = layout.getFacetedPropertyValue("decimals")
            if decimals is None:
                if width <= 7:
                    sqltype = "float"
                else:
                    sqltype = "real"
            else:
                decimals = int(decimals)
                if decimals == 0: 
                    if width <= 2:
                        sqltype = "tinyint"
                    elif width <= 4:
                        sqltype = "smallint"
                    elif width <= 6:
                        sqltype = "mediumint"
                    elif width <= 9:
                        sqltype = "int"
                    else:
                        sqltype = "bigint"
                else:
                    sqltype = "decimal("+str(width)+","+str(decimals)+")"
    elif datatype == "date":
        sqltype = "date"
    elif datatype == "datetime":
        sqltype = "datetime2"
    elif datatype.startswith("bool"):
        sqltype = "bit"
    return sqltype

def infer_mysql_datatype(layout):
    datatype = layout.getFacetedPropertyValue("datatype").lower()
    width = layout.getFacetedPropertyValue("width")
    sqltype = None
    if datatype == "text":
        sqltype = "text"
    elif datatype == "string":
        if width is None:
            width = 256
        sqltype = "varchar("+str(width)+")"
    elif datatype.startswith("int"):
        sqltype = "int"
    elif datatype.startswith("num") or datatype.startswith("decimal"):
        if width is None:
            sqltype = "double"
        else:
            decimals = layout.getFacetedPropertyValue("decimals")
            if decimals is None:
                decimals = "0"
            if int(decimals) == 0: 
                if int(width) <= 2:
                    sqltype = "tinyint"
                elif int(width) <= 4:
                    sqltype = "smallint"
                elif int(width) <= 6:
                    sqltype = "mediumint"
                elif int(width) <= 9:
                    sqltype = "int"
                elif int(width) <= 18:
                    sqltype = "bigint"
                else: 
                    sqltype = "decimal("+str(width)+","+str(decimals)+")"    
            else:   
                sqltype = "decimal("+str(width)+","+str(decimals)+")"
    elif datatype == "date":
        sqltype = "date"
    elif datatype.startswith("bool"):
        sqltype = "boolean"
    return sqltype

def infer_postgres_datatype(layout):
    datatype = layout.getFacetedPropertyValue("datatype").lower()
    width = layout.getFacetedPropertyValue("width")
    sqltype = None
    if datatype == "text":
        sqltype = "text"
    elif datatype == "string":
        if width is None:
            width = 256
        sqltype = "varchar("+str(width)+")"
    elif datatype.startswith("int"):
        sqltype = "int"
    elif datatype.startswith("num") or datatype.startswith("decimal"):
        if width is None:
            sqltype = "double precision"
        else:
            decimals = layout.getFacetedPropertyValue("decimals")
            if decimals is None:
                decimals = "0"
            if int(decimals) == 0: 
                if int(width) <= 4:
                    sqltype = "smallint"
                elif int(width) <= 9:
                    sqltype = "int"
                elif int(width) <= 18:
                    sqltype = "bigint"
                else: 
                    sqltype = "decimal("+str(width)+","+str(decimals)+")"       
            else:
                sqltype = "decimal("+str(width)+","+str(decimals)+")"       
    elif datatype == "date":
        sqltype = "date"
    elif datatype == "datetime":
        sqltype = "datetime"
    elif datatype == "time":
        sqltype = "time"
    elif datatype.startswith("bool"):
        sqltype = "boolean"
    else:
        print("ERROR: No datatype found")
    return sqltype

def infer_vertica_datatype(layout):
    datatype = layout.getFacetedPropertyValue("datatype").lower()
    width = layout.getFacetedPropertyValue("width")
    sqltype = None
    if datatype == "text":
        sqltype = "text"
    elif datatype == "string":
        if width is None:
            width = 256
        sqltype = "varchar("+str(width)+")"
    elif datatype.startswith("int"):
        sqltype = "int"
    elif datatype.startswith("num") or datatype.startswith("decimal"):
        if width is None:
            sqltype = "double"
        else:
            decimals = layout.getFacetedPropertyValue("decimals")
            if decimals is None:
                decimals = "0"
            if int(decimals) == 0: 
                if int(width) <= 4:
                    sqltype = "smallint"
                elif int(width) <= 9:
                    sqltype = "int"
                elif int(width) <= 18:
                    sqltype = "bigint"
                else: 
                    sqltype = "decimal("+str(width)+","+str(decimals)+")"       
            else:
                sqltype = "decimal("+str(width)+","+str(decimals)+")"       
    elif datatype == "date":
        sqltype = "date"
    elif datatype == "datetime":
        sqltype = "datetime"
    elif datatype == "time":
        sqltype = "time"
    elif datatype.startswith("bool"):
        sqltype = "boolean"
    else:
        print("ERROR: No datatype found")
    return sqltype

def escape(sql_flavor, str):
    """SQL string escaping"""
    return str
    

def get_quote_char(sql_flavor):
    if sql_flavor in ('athena','mysql'):
        return "`"
    else:
        return '"'
