package com.salpa.subject.mapper;

import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.salpa.subject.domain.*;
import com.salpa.subject.domain.converter.JsonConverter;
import com.salpa.subject.domain.data.*;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;

import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

@Mapper(componentModel = "spring", uses = JsonConverter.class)
public interface DatabaseMetaConverter {

    default DatabaseMeta of(MonitorDatabaseDocument database,
                            List<MonitorTableDocument> tables,
                            List<MonitorTableColumnDocument> columns,
                            List<MonitorTableIndexDocument> indexes,
                            List<MonitorTableTriggerDocument> triggers,
                            List<MonitorTableForeignKeyDocument> foreignKeys) {
        var columnMap = groupBy(columns, MonitorTableColumnDocument::getTableDocumentId);
        var indexMap = groupBy(indexes, MonitorTableIndexDocument::getTableDocumentId);
        var triggerMap = groupBy(triggers, MonitorTableTriggerDocument::getTableDocumentId);
        var fkMap = groupBy(foreignKeys, MonitorTableForeignKeyDocument::getTableDocumentId);
        return of(database, tables, columnMap, indexMap, triggerMap, fkMap);
    }

    default DatabaseMeta of(MonitorDatabaseDocument database,
                            List<MonitorTableDocument> tables,
                            Map<Integer, List<MonitorTableColumnDocument>> columnGroupByTableId,
                            Map<Integer, List<MonitorTableIndexDocument>> indexGroupByTableId,
                            Map<Integer, List<MonitorTableTriggerDocument>> triggerGroupByTableId,
                            Map<Integer, List<MonitorTableForeignKeyDocument>> fkGroupByTableId) {
        List<TableMeta> tableMetas = tables.stream()
                .map(table -> {
                    Integer id = table.getId();
                    var columns = columnGroupByTableId.getOrDefault(id, Collections.emptyList());
                    var indexes = indexGroupByTableId.getOrDefault(id, Collections.emptyList());
                    var triggers = triggerGroupByTableId.getOrDefault(id, Collections.emptyList());
                    var foreignKeys = fkGroupByTableId.getOrDefault(id, Collections.emptyList());
                    return of(table, columns, indexes, triggers, foreignKeys);
                })
                .collect(Collectors.toList());
        return of(database, tableMetas);
    }

    default DatabaseMeta of(MonitorDatabaseDocument database, List<TableMeta> tables) {
        DatabaseMeta databaseMeta = new DatabaseMeta();
        databaseMeta.setTables(tables);
        databaseMeta.setDatabaseName(database.getDatabaseName());
        databaseMeta.setSchemaName(database.getSchemaName());
        databaseMeta.setProductVersion(database.getProductVersion());
        databaseMeta.setProductName(database.getProductName());
        return databaseMeta;
    }

    default TableMeta of(MonitorTableDocument table,
                 List<MonitorTableColumnDocument> columns,
                 List<MonitorTableIndexDocument> indexes,
                 List<MonitorTableTriggerDocument> triggers,
                 List<MonitorTableForeignKeyDocument> foreignKeys){
        TableMeta tableMeta = new TableMeta();
        tableMeta.setName(table.getName());
        tableMeta.setType(table.getType());
        tableMeta.setComment(table.getComment() == null ? "" : table.getComment());
        List<ColumnMeta> columns1 = new ArrayList<>();
        for (MonitorTableColumnDocument column : columns) {
            columns1.add(of(column));
        }
        tableMeta.setColumns(columns1);
        List<IndexMeta> indexes1 = new ArrayList<>();
        for (MonitorTableIndexDocument index : indexes) {
            indexes1.add(of(index));
        }
        tableMeta.setIndexes(indexes1);
        List<TriggerMeta> triggers1 = new ArrayList<>();
        for (MonitorTableTriggerDocument trigger : triggers) {
            triggers1.add(of(trigger));
        }
        tableMeta.setTriggers(triggers1);
        List<ForeignKeyMeta> foreignKeys1 = new ArrayList<>();
        for (MonitorTableForeignKeyDocument foreignKey : foreignKeys) {
            foreignKeys1.add(of(foreignKey));
        }
        tableMeta.setForeignKeys(foreignKeys1);
        return tableMeta;
    }

    default ColumnMeta of(MonitorTableColumnDocument pojo) {
        ColumnMeta columnMeta = new ColumnMeta();
        columnMeta.setName(pojo.getName());
        columnMeta.setType(pojo.getType());
        columnMeta.setComment(pojo.getComment() == null ? "" : pojo.getComment());
        columnMeta.setAutoIncrement(pojo.getAutoIncrement());
        columnMeta.setDataType(pojo.getDataType());
        columnMeta.setDecimalDigits(pojo.getDecimalDigits());
        columnMeta.setDefaultValue(pojo.getDefaultValue());
        columnMeta.setIsPrimaryKey(pojo.getPrimaryKey());
        columnMeta.setNullable(pojo.getNullable());
        columnMeta.setSize(pojo.getSize());
        return columnMeta;
    }

    default IndexMeta of(MonitorTableIndexDocument pojo) {
        IndexMeta indexMeta = new IndexMeta();
        indexMeta.setName(pojo.getName());
        indexMeta.setIsUniqueKey(pojo.getUnique());
        indexMeta.setColumnNames(JSONObject.parseObject(pojo.getColumnNameArray(), List.class));
        return indexMeta;
    }

    default TriggerMeta of(MonitorTableTriggerDocument pojo) {
        TriggerMeta triggerMeta = new TriggerMeta();
        triggerMeta.setName(pojo.getName());
        triggerMeta.setTiming(pojo.getTiming());
        triggerMeta.setStatement(pojo.getStatement());
        triggerMeta.setManipulation(pojo.getManipulation());
        triggerMeta.setCreateAt(pojo.getTriggerCreateAt());
        return triggerMeta;
    }

    default ForeignKeyMeta of(MonitorTableForeignKeyDocument pojo) {
        ForeignKeyMeta foreignKeyMeta = new ForeignKeyMeta();
        foreignKeyMeta.setKeySeq(pojo.getKeySeq());
        foreignKeyMeta.setFkName(pojo.getFkName());
        foreignKeyMeta.setFkTableName(pojo.getFkTableName());
        foreignKeyMeta.setFkColumnName(pojo.getFkColumnName());
        foreignKeyMeta.setPkName(pojo.getPkName());
        foreignKeyMeta.setPkTableName(pojo.getPkTableName());
        foreignKeyMeta.setPkColumnName(pojo.getPkColumnName());
        foreignKeyMeta.setDeleteRule(pojo.getDeleteRule());
        foreignKeyMeta.setUpdateRule(pojo.getUpdateRule());
        return foreignKeyMeta;
    }

    List<TableMeta> of(List<TableDocumentResponse> table);

    TableMeta of(TableDocumentResponse table);

    default <R> Map<Integer, List<R>> groupBy(List<R> content, Function<R, Integer> idMapping) {
        return content.stream()
                .collect(Collectors.groupingBy(idMapping));
    }
}
