package com.salpa.subject.mapper;

import com.alibaba.fastjson2.JSON;
import com.salpa.subject.domain.*;
import com.salpa.subject.domain.converter.JsonConverter;
import com.salpa.subject.domain.data.DatabaseDocumentResponse;
import com.salpa.subject.domain.data.TableDocumentResponse;
import com.salpa.subject.domain.diff.*;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.ReportingPolicy;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Mapper(componentModel = "spring", uses = JsonConverter.class, unmappedTargetPolicy = ReportingPolicy.WARN)
public interface DocumentResponseConverter {

    @Mapping(target = "columns", source = "columns")
    @Mapping(target = "indexes", source = "indexes")
    @Mapping(target = "foreignKeys", source = "foreignKeys")
    @Mapping(target = "triggers", source = "triggers")
    default TableDocumentResponse of(MonitorTableDocument tableDocument,
                             Map<String, Integer> discussionCountMap,
                             Map<String, String> descriptionMap,
                             List<MonitorTableColumnDocument> columns,
                             List<MonitorTableIndexDocument> indexes,
                             List<MonitorTableForeignKeyDocument> foreignKeys,
                             List<MonitorTableTriggerDocument> triggers) {
        TableDocumentResponse tableDocumentResponse = new TableDocumentResponse();
        List<TableDocumentResponse.ColumnDocumentResponse> columns1 = of(columns, descriptionMap, discussionCountMap);
        tableDocumentResponse.setId(tableDocument.getId());
        tableDocumentResponse.setDatabaseDocumentId(tableDocument.getDatabaseDocumentId());
        tableDocumentResponse.setName(tableDocument.getName());
        tableDocumentResponse.setType(tableDocument.getType());
        tableDocumentResponse.setComment(tableDocument.getComment());
        tableDocumentResponse.setCreateAt(tableDocument.getCreateAt());
        tableDocumentResponse.setColumns(columns1);
        List<TableDocumentResponse.IndexDocumentResponse> indexes1 = new ArrayList<>();
        for (MonitorTableIndexDocument index : indexes) {
            TableDocumentResponse.IndexDocumentResponse indexDocumentResponse = new TableDocumentResponse.IndexDocumentResponse();
            indexDocumentResponse.setId(index.getId());
            indexDocumentResponse.setName(index.getName());
            indexDocumentResponse.setIsUnique(index.getUnique());
            indexDocumentResponse.setCreateAt(index.getCreateAt());
            indexDocumentResponse.setColumnNames(JSON.parseArray(index.getColumnNameArray(),String.class));
            indexes1.add(indexDocumentResponse);
        }
        tableDocumentResponse.setIndexes(indexes1);
        List<TableDocumentResponse.ForeignKeyDocumentResponse> foreignKeys1 = new ArrayList<>();
        for (MonitorTableForeignKeyDocument foreignKey : foreignKeys) {
            TableDocumentResponse.ForeignKeyDocumentResponse foreignKeyDocumentResponse = new TableDocumentResponse.ForeignKeyDocumentResponse();
            foreignKeyDocumentResponse.setId(foreignKey.getId());
            foreignKeyDocumentResponse.setKeySeq(foreignKey.getKeySeq());
            foreignKeyDocumentResponse.setFkName(foreignKey.getFkName());
            foreignKeyDocumentResponse.setFkTableName(foreignKey.getFkTableName());
            foreignKeyDocumentResponse.setFkColumnName(foreignKey.getFkColumnName());
            foreignKeyDocumentResponse.setPkName(foreignKey.getPkName());
            foreignKeyDocumentResponse.setPkTableName(foreignKey.getPkTableName());
            foreignKeyDocumentResponse.setPkColumnName(foreignKey.getPkColumnName());
            foreignKeyDocumentResponse.setUpdateRule(foreignKey.getUpdateRule());
            foreignKeyDocumentResponse.setDeleteRule(foreignKey.getDeleteRule());
            foreignKeyDocumentResponse.setCreateAt(foreignKey.getCreateAt());
            foreignKeys1.add(foreignKeyDocumentResponse);
        }
        tableDocumentResponse.setForeignKeys(foreignKeys1);
        List<TableDocumentResponse.TriggerDocumentResponse> triggers1 = new ArrayList<>();
        for (MonitorTableTriggerDocument trigger : triggers) {
            TableDocumentResponse.TriggerDocumentResponse triggerDocumentResponse = new TableDocumentResponse.TriggerDocumentResponse();
            triggerDocumentResponse.setId(trigger.getId());
            triggerDocumentResponse.setName(trigger.getName());
            triggerDocumentResponse.setTiming(trigger.getTiming());
            triggerDocumentResponse.setStatement(trigger.getStatement());
            triggerDocumentResponse.setManipulation(trigger.getManipulation());
            triggerDocumentResponse.setTriggerCreateAt(trigger.getTriggerCreateAt());
            triggerDocumentResponse.setCreateAt(trigger.getCreateAt());
            triggers1.add(triggerDocumentResponse);
        }
        tableDocumentResponse.setTriggers(triggers1);
        return tableDocumentResponse;
    }

    @Mapping(target = "columns", source = "columns")
    @Mapping(target = "indexes", source = "indexes")
    @Mapping(target = "foreignKeys", source = "foreignKeys")
    @Mapping(target = "triggers", source = "triggers")
    @SuppressWarnings("checkstyle:all")
    TableDocumentResponse of(MonitorTableDocument tableDocument,
                             Integer discussionCount,
                             String description,
                             List<MonitorTableColumnDocument> columns,
                             List<MonitorTableIndexDocument> indexes,
                             List<MonitorTableForeignKeyDocument> foreignKeys,
                             List<MonitorTableTriggerDocument> triggers);

    default TableDocumentResponse.ColumnDocumentResponse of(MonitorTableColumnDocument pojo,
                                                    String description,Integer count) {
        TableDocumentResponse.ColumnDocumentResponse columnDocumentResponse = new TableDocumentResponse.ColumnDocumentResponse();
        columnDocumentResponse.setId(pojo.getId());
        columnDocumentResponse.setName(pojo.getName());
        columnDocumentResponse.setType(pojo.getType());
        columnDocumentResponse.setSize(pojo.getSize());
        columnDocumentResponse.setDecimalDigits(pojo.getDecimalDigits());
        columnDocumentResponse.setComment(pojo.getComment());
        columnDocumentResponse.setIsPrimaryKey(pojo.getPrimaryKey());
        columnDocumentResponse.setNullable(pojo.getNullable());
        columnDocumentResponse.setAutoIncrement(pojo.getAutoIncrement());
        columnDocumentResponse.setDefaultValue(pojo.getDefaultValue());
        columnDocumentResponse.setCreateAt(pojo.getCreateAt());
        columnDocumentResponse.setDescription(description);
        columnDocumentResponse.setDiscussionCount(count);
        return columnDocumentResponse;
    }

    default List<TableDocumentResponse.ColumnDocumentResponse> of(
            List<MonitorTableColumnDocument> columns,
            Map<String, String> descriptionMapByJoinName,
            Map<String, Integer> discussionCountMapByJoinName) {
        return columns.stream()
                .map(column -> {
                    String description = descriptionMapByJoinName.get(column.getName());
                    Integer count = discussionCountMapByJoinName.get(column.getName());
                    return of(column, description,count);
                })
                .collect(Collectors.toList());
    }

    @Mapping(target = "columnNames", source = "columnNameArray")
    TableDocumentResponse.IndexDocumentResponse of(MonitorTableIndexDocument indexDocument);

    @Mapping(target = "id", source = "databaseDocument.id")
    @Mapping(target = "createAt", source = "databaseDocument.createAt")
    @Mapping(target = "documentVersion", source = "databaseDocument.version")
    DatabaseDocumentResponse of(MonitorDatabaseDocument databaseDocument,
                                List<TableDocumentResponse> tables);

    default TableDocumentResponse ofDiff(TableDocDiff table,
                                         Map<String, Integer> discussionCountMap,
                                         Map<String, String> descriptionMap) {
        List<TableDocumentResponse.ColumnDocumentResponse> cols = toColumns(table, descriptionMap, discussionCountMap);
        List<TableDocumentResponse.IndexDocumentResponse> inds = toIndexes(table);
        List<TableDocumentResponse.TriggerDocumentResponse> tris = toTriggers(table);
        List<TableDocumentResponse.ForeignKeyDocumentResponse> fors = toForeignKeys(table);
        return ofDiff(table, cols, inds, tris, fors, discussionCountMap, descriptionMap);
    }

    default TableDocumentResponse ofDiff(TableDocDiff table,
                                 List<TableDocumentResponse.ColumnDocumentResponse> cols,
                                 List<TableDocumentResponse.IndexDocumentResponse> inds,
                                 List<TableDocumentResponse.TriggerDocumentResponse> tris,
                                 List<TableDocumentResponse.ForeignKeyDocumentResponse> fors,
                                 Map<String, Integer> discussionCountMap,
                                 Map<String, String> descriptionMap) {
        TableDocumentResponse tableDocumentResponse = new TableDocumentResponse();
        tableDocumentResponse.setId(table.getId());
        tableDocumentResponse.setName(table.getName());
        tableDocumentResponse.setType(table.getType());
        tableDocumentResponse.setComment(table.getComment());
        tableDocumentResponse.setCreateAt(table.getCreateAt());
        tableDocumentResponse.setDiffType(table.getDiffType());
        for (TableDocumentResponse.ColumnDocumentResponse col : cols) {
            col.setDescription(descriptionMap.get(col.getName()));
            col.setDiscussionCount(discussionCountMap.get(col.getName()));
        }
        tableDocumentResponse.setColumns(cols);
        tableDocumentResponse.setIndexes(inds);
        tableDocumentResponse.setTriggers(tris);
        tableDocumentResponse.setForeignKeys(fors);
        return tableDocumentResponse;
    }

    default List<TableDocumentResponse.ColumnDocumentResponse> toColumns(TableDocDiff table,
                                                                         Map<String, String> descriptionMap,
                                                                         Map<String, Integer> discussionCountMap) {
        return table.getColumns()
                .stream()
                .map(col -> toColumn(col, descriptionMap, discussionCountMap))
                .collect(Collectors.toList());
    }

    default List<TableDocumentResponse.IndexDocumentResponse> toIndexes(TableDocDiff table) {
        return table.getIndexes()
                .stream()
                .map(ind -> toIndex(ind))
                .collect(Collectors.toList());
    }

    default List<TableDocumentResponse.TriggerDocumentResponse> toTriggers(TableDocDiff table) {
        return table.getTriggers()
                .stream()
                .map(tri -> toTrigger(tri))
                .collect(Collectors.toList());
    }

    default List<TableDocumentResponse.ForeignKeyDocumentResponse> toForeignKeys(TableDocDiff table) {
        return table.getForeignKeys()
                .stream()
                .map(foreign -> toForeignKey(foreign))
                .collect(Collectors.toList());
    }

    @Mapping(target = "description", expression = "java(descriptionMap.get(tableName+\".\"+diff.getName()))")
    @Mapping(target = "original",
            expression = "java(toOriginalColumn(tableName, diff.getOriginal(), descriptionMap))")
    default TableDocumentResponse.ColumnDocumentResponse toColumn(ColumnDocDiff diff,
                                                          Map<String, String> descriptionMap,
                                                          Map<String, Integer> discussionCountMap) {
        TableDocumentResponse.ColumnDocumentResponse columnDocumentResponse = new TableDocumentResponse.ColumnDocumentResponse();
        columnDocumentResponse.setId(diff.getId());
        columnDocumentResponse.setName(diff.getName());
        columnDocumentResponse.setType(diff.getType());
        columnDocumentResponse.setSize(diff.getSize());
        columnDocumentResponse.setDecimalDigits(diff.getDecimalDigits());
        columnDocumentResponse.setComment(diff.getComment());
        columnDocumentResponse.setIsPrimaryKey(diff.getIsPrimaryKey());
        columnDocumentResponse.setNullable(diff.getNullable());
        columnDocumentResponse.setAutoIncrement(diff.getAutoIncrement());
        columnDocumentResponse.setDefaultValue(diff.getDefaultValue());
        columnDocumentResponse.setCreateAt(diff.getCreateAt());
        columnDocumentResponse.setDescription(descriptionMap.get(diff.getName()));
        columnDocumentResponse.setDiscussionCount(discussionCountMap.get(diff.getName()));
        columnDocumentResponse.setDiffType(diff.getDiffType());
        columnDocumentResponse.setOriginal(toOriginalColumn(diff.getOriginal(),descriptionMap,discussionCountMap));
        return columnDocumentResponse;
    }

    default TableDocumentResponse.IndexDocumentResponse toIndex(IndexDocDiff diff) {
        if (diff == null) {
            return null;
        } else {
            TableDocumentResponse.IndexDocumentResponse indexDocumentResponse = new TableDocumentResponse.IndexDocumentResponse();
            indexDocumentResponse.setId(diff.getId());
            indexDocumentResponse.setName(diff.getName());
            indexDocumentResponse.setIsUnique(diff.getIsUnique());
            indexDocumentResponse.setDiffType(diff.getDiffType());
            indexDocumentResponse.setColumnNames(JSON.parseObject(diff.getColumnNameArray(),List.class));
            indexDocumentResponse.setCreateAt(diff.getCreateAt());
            indexDocumentResponse.setOriginal(toIndex(diff.getOriginal()));
            return indexDocumentResponse;
        }
    }

    default TableDocumentResponse.TriggerDocumentResponse toTrigger(TriggerDocDiff diff) {
        if (diff == null) {
            return null;
        } else {
            TableDocumentResponse.TriggerDocumentResponse triggerDocumentResponse = new TableDocumentResponse.TriggerDocumentResponse();
            triggerDocumentResponse.setId(diff.getId());
            triggerDocumentResponse.setName(diff.getName());
            triggerDocumentResponse.setTiming(diff.getTiming());
            triggerDocumentResponse.setDiffType(diff.getDiffType());
            triggerDocumentResponse.setStatement(diff.getStatement());
            triggerDocumentResponse.setManipulation(diff.getManipulation());
            triggerDocumentResponse.setTriggerCreateAt(diff.getTriggerCreateAt());
            triggerDocumentResponse.setCreateAt(diff.getCreateAt());
            triggerDocumentResponse.setOriginal(toTrigger(diff.getOriginal()));
            return triggerDocumentResponse;
        }
    }

    default TableDocumentResponse.ForeignKeyDocumentResponse toForeignKey(ForeignKeyDocDiff diff) {
        if (diff == null) {
            return null;
        } else {
            TableDocumentResponse.ForeignKeyDocumentResponse foreignKeyDocumentResponse = new TableDocumentResponse.ForeignKeyDocumentResponse();
            foreignKeyDocumentResponse.setId(diff.getId());
            foreignKeyDocumentResponse.setKeySeq(diff.getKeySeq());
            foreignKeyDocumentResponse.setCreateAt(diff.getCreateAt());
            foreignKeyDocumentResponse.setDiffType(diff.getDiffType());
            foreignKeyDocumentResponse.setFkName(diff.getFkName());
            foreignKeyDocumentResponse.setFkTableName(diff.getFkTableName());
            foreignKeyDocumentResponse.setFkColumnName(diff.getFkColumnName());
            foreignKeyDocumentResponse.setPkName(diff.getPkName());
            foreignKeyDocumentResponse.setPkTableName(diff.getPkTableName());
            foreignKeyDocumentResponse.setPkColumnName(diff.getPkColumnName());
            foreignKeyDocumentResponse.setDeleteRule(diff.getDeleteRule());
            foreignKeyDocumentResponse.setUpdateRule(diff.getUpdateRule());
            foreignKeyDocumentResponse.setCreateAt(diff.getCreateAt());
            foreignKeyDocumentResponse.setOriginal(toForeignKey(diff.getOriginal()));
            return foreignKeyDocumentResponse;
        }
    }

    default TableDocumentResponse.ColumnDocumentResponse toOriginalColumn(ColumnDocDiff diff,
                                                                          Map<String, String> descriptionMap,
                                                                          Map<String, Integer> discussionCountMap) {
        if (diff == null) {
            return null;
        }
        return toColumn(diff, descriptionMap,discussionCountMap);
    }

    default TableDocumentResponse.IndexDocumentResponse toOriginalIndex(IndexDocDiff diff) {
        if (diff == null) {
            return null;
        }
        return toIndex(diff);
    }

    default TableDocumentResponse.TriggerDocumentResponse toOriginalTrigger(TriggerDocDiff diff) {
        if (diff == null) {
            return null;
        }
        return toTrigger(diff);
    }

    default TableDocumentResponse.ForeignKeyDocumentResponse toOriginalForeignKey(ForeignKeyDocDiff diff) {
        if (diff == null) {
            return null;
        }
        return toForeignKey(diff);
    }
}
