from tools_hjh import OracleTools, Tools
import re


def get_table_ddl_pg(dba_conn, username, table, forever_number_to_numeric=True, to_lower=True):
    sql_list = []
    metadata = OracleTools.get_table_metadata(dba_conn, username, table)
    if metadata == {}:
        return sql_list
    
    if to_lower:
        create_table_sql = 'create table ' + username + '."' + metadata['name'].lower() + '"'
    else:
        create_table_sql = 'create table ' + username + '."' + metadata['name'] + '"'
    
    create_table_sql = create_table_sql + '\n(\n'
    for col in metadata['columns']:
        if to_lower:
            name = str(col['name']).lower().strip()
        else:
            name = str(col['name']).strip()
        type_ = str(col['type'])
        
        # 列类型
        if 'VARCHAR' in type_:
            type_ = type_.replace('VARCHAR2', 'VARCHAR')
            type_ = type_.replace('NVARCHAR', 'VARCHAR')
            type_ = type_.replace('NVARCHAR2', 'VARCHAR')
        elif 'CHAR' in type_:
            type_ = type_.replace('NCHAR', 'CHAR')
        elif 'NUMBER' in type_:
            if '(' in type_ and ',' in type_:
                size_1 = int(type_.split('(')[1].split(')')[0].split(',')[0].strip())
                size_2 = int(type_.split('(')[1].split(')')[0].split(',')[1].strip())
                if size_2 > size_1:
                    type_ = 'NUMERIC'
                else:
                    type_ = type_.replace('NUMBER', 'NUMERIC')
            elif not forever_number_to_numeric and '(' in type_ and ',' not in type_:
                size_ = int(type_.split('(')[1].split(')')[0])
                if size_ <= 4:
                    type_ = 'SMALLINT'
                elif size_ > 4 and size_ <= 9: 
                    type_ = 'INT'
                elif size_ > 9 and size_ <= 18: 
                    type_ = 'BIGINT'
                else:
                    type_ = type_.replace('NUMBER', 'NUMERIC')
            else:
                type_ = type_.replace('NUMBER', 'NUMERIC')
        elif 'RAW' in type_:
            if '(' in type_:
                size_ = int(type_.split('(')[1].split(')')[0])
                if size_ == 16:
                    # type_ = 'VARCHAR(32)'
                    type_ = 'UUID'
                else:
                    type_ = 'BYTEA'
            else:
                type_ = 'BYTEA'
        elif type_.startswith('TIMESTAMP(') and 'WITH LOCAL TIME ZONE' in type_:
            type_ = 'TIMESTAMPTZ'
        elif type_.startswith('INTERVAL DAY('):
            second_ = type_.split(' TO ')[1].strip()
            type_ = 'INTERVAL DAY TO ' + second_
        else:
            type_ = type_.replace('BINARY_INTEGER', 'INTEGER')
            type_ = type_.replace('BINARY_FLOAT', 'FLOAT')
            type_ = type_.replace('DATE', 'TIMESTAMP(0)')
            type_ = type_.replace('NCLOB', 'TEXT')
            type_ = type_.replace('CLOB', 'TEXT')
            type_ = type_.replace('LONG', 'TEXT')
            type_ = type_.replace('BLOB', 'BYTEA')
            type_ = type_.replace('LONG RAW', 'BYTEA')
            
            # 暂定
            type_ = type_.replace('UROWID', 'VARCHAR(18)')
            type_ = type_.replace('ROWID', 'VARCHAR(18)')
            
        # 默认值
        if col['default_value'] != None:
            # to_number
            if 'to_number(' in col['default_value'].lower():
                to_number_content = Tools.get_fun_content(col['default_value'], 'to_number'.upper())
                a1 = col['default_value'].split('TO_NUMBER(' + to_number_content + ')')[0]
                a2 = col['default_value'].split('TO_NUMBER(' + to_number_content + ')')[1]
                col['default_value'] = a1 + to_number_content + '::numeric' + a2
            
            # ''值不可在非字符类型上
            if col['default_value'].strip() == "''" and ('VARCHAR' not in type_ or type_ != 'TEXT'):
                col['default_value'] = 'NULL'
                
            col['default_value'] = re.sub('sys_guid', 'uuid_generate_v4', col['default_value'], flags=re.IGNORECASE)
            col['default_value'] = re.sub('systimestamp', 'current_timestamp', col['default_value'], flags=re.IGNORECASE)
            col['default_value'] = re.sub('sysdate', 'statement_timestamp()', col['default_value'], flags=re.IGNORECASE)
            
            # 时间加减
            if str(col['default_value']).startswith('statement_timestamp()-'):
                jz = str(col['default_value']).split('-')[1].strip()
                col['default_value'] = "statement_timestamp() - interval '" + jz + " days'"
            elif str(col['default_value']).startswith('statement_timestamp()+'):
                jz = str(col['default_value']).split('+')[1].strip()
                col['default_value'] = "statement_timestamp() + interval '" + jz + " days'"
                
            # 虚拟列
            if col['virtual'] == 'NO':
                default_value = 'default ' + str(col['default_value']).strip()
            else:
                for col2 in metadata['columns']:
                    col2_name = str(col2['name'])
                    if '"' + col2_name.upper() + '"' in str(col['default_value']):
                        if to_lower:
                            col['default_value'] = col['default_value'].replace(col2_name.upper(), col2_name.lower())
                        else:
                            col['default_value'] = col['default_value'].replace(col2_name.upper(), col2_name)
                default_value = 'generated always as (' + col['default_value'] + ') stored'
        else:
            default_value = ''

        # 非空
        if col['nullable']:
            nullable = ''
        else:
            nullable = 'not null'
            
        if col['virtual'] == 'NO':
            create_table_sql = create_table_sql + '[--gt--]"' + name + '"' + ' ' + type_ + ' ' + default_value + ' ' + nullable + ',\n'
        else:
            create_table_sql = create_table_sql + '[--gt--]"' + name + '"' + ' ' + type_ + ' ' + default_value + ',\n'
        
    create_table_sql = Tools.merge_spaces(create_table_sql.rstrip().rstrip(',')).replace(' ,', ',').replace('[--gt--]', '  ')
    create_table_sql = create_table_sql + '\n)'
    
    pk_add_cols_list = []
    
    partition_columns = ''
    partitions = []
    if metadata['partition'] != None:
        partition = metadata['partition']
        partitions = partition['partitions']
        partition_type = partition['partition_type']
        partition_columns = partition['partition_columns']
        for col in partition_columns.split(','):
            if to_lower:
                col = col.strip().lower()
            else:
                col = col.strip()
            if col not in pk_add_cols_list:
                pk_add_cols_list.append(col)
        subpartition_type = partition['subpartition_type']
        subpartition_columns = partition['subpartition_columns']
        if to_lower:
            create_table_sql = create_table_sql + ' partition by ' + partition_type + ' (' + partition_columns.lower() + ')'
        else:
            create_table_sql = create_table_sql + ' partition by ' + partition_type + ' (' + partition_columns + ')'
    sql_list.append(create_table_sql)    
    
    # 分区
    q_val = None
    idx = 0
    for part in partitions:
        if to_lower:
            name = (table + '_part' + str(idx)).lower()
        else:
            name = (table + '_part' + str(idx))
        value = part['value']
        if partition_type == 'RANGE':
            if value.startswith("TIMESTAMP' "):
                value = value.replace("TIMESTAMP", '')
            if value.startswith("TO_DATE(' "):
                value = value.split(',')[0].replace('TO_DATE(', '')
            if q_val is None:
                q_val = 'MINVALUE'
            if to_lower:
                part_sql = 'create table ' + username + '."' + name + '" partition of ' + username + '."' + table.lower() + '" for values from (' + q_val + ') to (' + value + ')'
            else:
                part_sql = 'create table ' + username + '."' + name + '" partition of ' + username + '."' + table + '" for values from (' + q_val + ') to (' + value + ')'
            q_val = value
        elif partition_type == 'HASH':
            if to_lower:
                part_sql = 'create table ' + username + '."' + name + '" partition of ' + username + '."' + table.lower() + '" for values with (modulus ' + str(len(partitions)) + ', remainder ' + str(idx) + ')'
            else:
                part_sql = 'create table ' + username + '."' + name + '" partition of ' + username + '."' + table + '" for values with (modulus ' + str(len(partitions)) + ', remainder ' + str(idx) + ')'
        elif partition_type == 'LIST':
            if value.lower() == 'default':
                if to_lower:
                    part_sql = 'create table ' + username + '."' + name + '" partition of ' + username + '."' + table.lower() + '" default'
                else:
                    part_sql = 'create table ' + username + '."' + name + '" partition of ' + username + '."' + table + '" default'
            else:
                if to_lower:
                    part_sql = 'create table ' + username + '."' + name + '" partition of ' + username + '."' + table.lower() + '" for values in (' + value + ')'
                else:
                    part_sql = 'create table ' + username + '."' + name + '" partition of ' + username + '."' + table + '" for values in (' + value + ')'
        if subpartition_type is not None:
            for col in subpartition_columns.split(','):
                if to_lower:
                    col = col.strip().lower()
                else:
                    col = col.strip()
                if col not in pk_add_cols_list:
                    pk_add_cols_list.append(col)
            if to_lower:
                part_sql = part_sql + ' partition by ' + subpartition_type.lower() + ' (' + subpartition_columns.lower() + ')'
            else:
                part_sql = part_sql + ' partition by ' + subpartition_type + ' (' + subpartition_columns + ')'
        idx = idx + 1
        sql_list.append(part_sql)
        
    # 子分区
    q_val = None
    idx = 0
    for part in partitions:
        sub_idx = 0
        if to_lower:
            par_name = (table + '_part' + str(idx)).lower()
        else:
            par_name = (table + '_part' + str(idx))
        for subpar in part['subpartitions']:
            if to_lower:
                name = (par_name + '_subpart' + str(sub_idx)).lower()
            else:
                name = (par_name + '_subpart' + str(sub_idx))
            value = subpar['value']
            if subpartition_type == 'RANGE':
                if value.startswith("TIMESTAMP' "):
                    value = value.replace("TIMESTAMP", '')
                if value.startswith("TO_DATE(' "):
                    value = value.split(',')[0].replace('TO_DATE(', '')
                if q_val is None:
                    q_val = 'MINVALUE'
                if to_lower:
                    subpar_sql = 'create table ' + username + '."' + name + '" partition of ' + username + '."' + par_name.lower() + '" for values from (' + q_val + ') to (' + value + ')'
                else:
                    subpar_sql = 'create table ' + username + '."' + name + '" partition of ' + username + '."' + par_name + '" for values from (' + q_val + ') to (' + value + ')'
                q_val = value
            elif subpartition_type == 'HASH':
                if to_lower:
                    subpar_sql = 'create table ' + username + '."' + name + '" partition of ' + username + '."' + par_name.lower() + '" for values with (modulus ' + str(len(part['subpartitions'])) + ', remainder ' + str(sub_idx) + ')'
                else:
                    subpar_sql = 'create table ' + username + '."' + name + '" partition of ' + username + '."' + par_name + '" for values with (modulus ' + str(len(part['subpartitions'])) + ', remainder ' + str(sub_idx) + ')'
            elif subpartition_type == 'LIST':
                if value.lower() == 'default':
                    if to_lower:
                        subpar_sql = 'create table ' + username + '."' + name + '" partition of ' + username + '."' + par_name.lower() + '" default'
                    else:
                        subpar_sql = 'create table ' + username + '."' + name + '" partition of ' + username + '."' + par_name + '" default'
                else:
                    if to_lower:
                        subpar_sql = 'create table ' + username + '."' + name + '" partition of ' + username + '."' + par_name.lower() + '" for values in (' + value + ')'
                    else:
                        subpar_sql = 'create table ' + username + '."' + name + '" partition of ' + username + '."' + par_name + '" for values in (' + value + ')'
            sub_idx = sub_idx + 1
            sql_list.append(subpar_sql)
        idx = idx + 1
        
    # 注释
    if metadata['comments'] != None:
        comments_str = ''
        if to_lower:
            comments_str = 'comment on table ' + username + '."' + table.lower() + '"' + " is '" + metadata['comments'].replace('\\u', '\\\\u').replace("'", "''") + "'"
        else:
            comments_str = 'comment on table ' + username + '."' + table + '"' + " is '" + metadata['comments'].replace('\\u', '\\\\u').replace("'", "''") + "'"
        sql_list.append(comments_str)
        
    for col in metadata['columns']:
        comments_str = ''
        if to_lower:
            name = col['name'].lower().strip()
        else:
            name = col['name'].strip()
        comments = col['comments']
        if comments != None: 
            if to_lower:
                comments_str = comments_str + 'comment on column ' + username + '."' + table.lower() + '"."' + name.lower() + '"' + " is '" + comments.replace('\\u', '\\\\u').replace("'", "''") + "'"
            else:
                comments_str = comments_str + 'comment on column ' + username + '."' + table + '"."' + name + '"' + " is '" + comments.replace('\\u', '\\\\u').replace("'", "''") + "'"
            sql_list.append(comments_str)
            
    # 索引
    for idx in metadata['indexes']:
        index_str = ''
        name = idx['name']
        type_ = idx['type']
        my_cols_ = idx['columns']
        
        my_cols = ''
        for col in my_cols_.split('SC,'):
            is_asc_or_desc = ''
            if col.endswith(' A') or col.endswith(' ASC'):
                is_asc_or_desc = 'ASC'
            elif col.endswith(' DESC') or col.endswith(' DE'):
                is_asc_or_desc = 'DESC'
            col = col.replace(' ASC', '').replace(' DESC', '').replace(' A', '').replace(' DE', '').strip()
            
            # 索引字段是数字
            if col.isdigit():
                col = '(' + col + ') ' + is_asc_or_desc
            # 索引字段是字符串（列写错成字符串）
            if col.startswith("'") and col.endswith("'"):
                if to_lower:
                    col = '"' + col.strip("'").lower() + '" ' + is_asc_or_desc
                else:
                    col = '"' + col.strip("'") + '" ' + is_asc_or_desc
            # 索引字段是 case when then
            if 'case' in col.lower() and 'when' in col.lower() and 'then' in col.lower():
                col = '(' + col + ') ' + is_asc_or_desc
            # 索引字段是||连接
            if '||' in col:
                col = '(' + col + ') ' + is_asc_or_desc
            # 索引字段是 trunc() 函数
            if 'trunc(' in col:
                fun_content = Tools.get_fun_content(col, 'trunc')
                col = col.split('trunc')[0] + "date_trunc('day', " + fun_content + col.split(fun_content)[1] + ' ' + is_asc_or_desc
            if 'TRUNC(' in col:
                fun_content = Tools.get_fun_content(col, 'TRUNC')
                col = col.split('TRUNC')[0] + "date_trunc('day', " + fun_content + col.split(fun_content)[1] + ' ' + is_asc_or_desc
            # 索引字段是 to_number() 函数
            if 'to_number(' in col:
                fun_content = Tools.get_fun_content(col, 'to_number')
                col = col.split('to_number')[0] + '(' + fun_content + '::numeric ' + col.split(fun_content)[1] + ' ' + is_asc_or_desc
            if 'TO_NUMBER(' in col:
                fun_content = Tools.get_fun_content(col, 'TO_NUMBER')
                col = col.split('TO_NUMBER')[0] + '(' + fun_content + '::numeric ' + col.split(fun_content)[1] + ' ' + is_asc_or_desc
            # 索引字段是 to_char() 函数，且不是日期格式化
            if 'to_char(' in col and ',' not in col:
                fun_content = Tools.get_fun_content(col, 'to_char')
                col = col.split('to_char')[0] + '(' + fun_content + '::varchar ' + col.split(fun_content)[1] + ' ' + is_asc_or_desc
            if 'TO_CHAR(' in col and ',' not in col:
                fun_content = Tools.get_fun_content(col, 'TO_CHAR')
                col = col.split('TO_CHAR')[0] + '(' + fun_content + '::varchar ' + col.split(fun_content)[1] + ' ' + is_asc_or_desc
            # 索引字段是 sys_op_c2c() 函数
            if 'sys_op_c2c(' in col:
                fun_content = Tools.get_fun_content(col, 'sys_op_c2c')
                col = col.split('sys_op_c2c')[0] + fun_content + col.split(fun_content + ')')[1] + ' ' + is_asc_or_desc
            if 'SYS_OP_C2C(' in col:
                fun_content = Tools.get_fun_content(col, 'SYS_OP_C2C')
                col = col.split('SYS_OP_C2C')[0] + fun_content + col.split(fun_content + ')')[1] + ' ' + is_asc_or_desc
            
            my_cols = my_cols + col + ', '
        my_cols = my_cols[:-2]
        
        if type_ == 'UNIQUE' and metadata['partition'] != None:
            cols_partition_columns = ''
            my_cols_list = []
            for col in my_cols.split(','):
                if not (col.lower().startswith('(case ') and 'when' in col.lower() and 'then' in col.lower()):
                    if to_lower:
                        col = col.strip().lower()
                    else:
                        col = col.strip()
                    if ' ' in col:
                        col = col.split(' ')[0]
                    if col not in my_cols_list:
                        my_cols_list.append(col)
                else:
                    if to_lower:
                        col = col.strip().lower()
                    else:
                        col = col.strip()
                    if ') ' in col:
                        col = col.split(') ')[0] + ')'
                    if col not in my_cols_list:
                        my_cols_list.append(col)
            pk_cols_list = my_cols_list.copy()
            for col in pk_add_cols_list:
                if col not in pk_cols_list:
                    pk_cols_list.append(col)
            for col in pk_cols_list:
                cols_partition_columns = cols_partition_columns + col + ', '
            cols_partition_columns = cols_partition_columns[:-2]
            
        if type_ == 'NORMAL':
            type_ = ''
            
        if type_ == 'UNIQUE' and metadata['partition'] != None:
            cols = cols_partition_columns
        else:
            cols = my_cols.replace(',', ', ')
        
        if to_lower:
            index_str = index_str + 'create ' + type_ + ' index "' + name.lower() + '" on ' + username + '."' + table.lower() + '" (' + cols.lower() + ') '
        else:
            index_str = index_str + 'create ' + type_ + ' index "' + name + '" on ' + username + '."' + table + '" (' + cols + ') '
            
        index_str = Tools.merge_spaces(index_str)
        index_str = index_str.replace(' asc', '')
        sql_list.append(index_str)

    # 约束（主键、唯一、外键）
    for constraint in metadata['constraints']:
        k_str = ''
        name = constraint['name']
        my_cols_ = constraint['columns']
        
        my_cols = ''
        for col in my_cols_.split(','):
            if col.replace(' ASC', '').replace(' DESC', '').strip().isdigit() and len(col.split(' ')) == 2:
                col = '(' + col.split(' ')[0] + ') ' + col.split(' ')[1]
            elif 'case' in col.lower() and 'when' in col.lower() and 'then' in col.lower() and 'else' in col.lower():
                col = '(' + col + ') '
            my_cols = my_cols + col + ', '
        my_cols = my_cols[:-2]
        
        type_ = constraint['type']
        r_table = constraint['r_table']
        r_cols = constraint['r_cols']
        
        if type_ == 'primary_key' or type_ == 'unique':
            cols_partition_columns = ''
            my_cols_list = []
            for col in my_cols.split(','):
                if to_lower:
                    col = col.strip().lower()
                else:
                    col = col.strip()
                if ' ' in col:
                    col = col.split(' ')[0]
                if col not in my_cols_list:
                    my_cols_list.append(col)
            pk_cols_list = my_cols_list.copy()
            for col in pk_add_cols_list:
                if col not in pk_cols_list:
                    pk_cols_list.append(col)
            for col in pk_cols_list:
                cols_partition_columns = cols_partition_columns + col + ', '
            cols_partition_columns = cols_partition_columns[:-2]
        
        if type_ == 'primary_key' and metadata['partition'] != None:
            if to_lower:
                k_str = k_str + 'alter table ' + username + '."' + table.lower() + '" add constraint ' + name + ' primary key (' + cols_partition_columns.lower() + ')'
            else:
                k_str = k_str + 'alter table ' + username + '."' + table + '" add constraint ' + name + ' primary key (' + cols_partition_columns + ')'
        if type_ == 'primary_key' and metadata['partition'] == None:
            if to_lower:
                k_str = k_str + 'alter table ' + username + '."' + table.lower() + '" add constraint ' + name + ' primary key (' + my_cols.lower() + ')'
            else:
                k_str = k_str + 'alter table ' + username + '."' + table + '" add constraint ' + name + ' primary key (' + my_cols + ')'
        elif type_ == 'unique' and metadata['partition'] != None:
            if to_lower:
                k_str = k_str + 'alter table ' + username + '."' + table.lower() + '" add constraint ' + name + ' unique (' + cols_partition_columns.lower() + ')'
            else:
                k_str = k_str + 'alter table ' + username + '."' + table + '" add constraint ' + name + ' unique (' + cols_partition_columns + ')'
        elif type_ == 'unique' and metadata['partition'] == None:
            if to_lower:
                k_str = k_str + 'alter table ' + username + '."' + table.lower() + '" add constraint ' + name + ' unique (' + my_cols.lower() + ')'
            else:
                k_str = k_str + 'alter table ' + username + '."' + table + '" add constraint ' + name + ' unique (' + my_cols + ')'
        elif type_ == 'foreign_key':
            if to_lower:
                k_str = k_str + 'alter table ' + username + '."' + table.lower() + '" add constraint ' + name + ' foreign key (' + my_cols.lower() + ') references ' + r_table.lower() + ' (' + r_cols.lower() + ')'
            else:
                k_str = k_str + 'alter table ' + username + '."' + table + '" add constraint ' + name + ' foreign key (' + my_cols + ') references ' + r_table + ' (' + r_cols + ')'
            delete_rule = constraint['delete_rule']
            if delete_rule == 'CASCADE':
                k_str = k_str + ' on delete cascade'
            elif delete_rule == 'SET NULL':
                k_str = k_str + ' on delete set null'
        sql_list.append(k_str)
    return sql_list
