package com.aicheck.analyzer;

import com.aicheck.model.FieldChange;
import com.aicheck.model.FieldInfo;

import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * 对比新旧字段列表，产出新增/删除/类型修改/重命名（纯注释变更忽略）。
 */
public class FieldDiffEngine {

    /**
     * 按字段名对比；删除+新增且说明匹配时合并为重命名。
     * 输出顺序：按新字段声明顺序，未配对的删除字段置于末尾。
     */
    public List<FieldChange> diff(List<FieldInfo> oldFields, List<FieldInfo> newFields) {
        Map<String, FieldInfo> oldMap = toMap(oldFields);
        Map<String, FieldInfo> newMap = toMap(newFields);

        List<FieldChange> modified = new ArrayList<>();
        List<FieldInfo> added = new ArrayList<>();
        List<FieldInfo> removed = new ArrayList<>();

        for (FieldInfo newField : newFields) {
            FieldInfo oldField = oldMap.get(newField.getName());
            if (oldField == null) {
                added.add(newField);
            } else if (!oldField.getType().equals(newField.getType())) {
                modified.add(FieldChange.modified(oldField, newField, buildTypeDetail(oldField, newField)));
            }
            // 仅 @Schema / 注释文案变化：不纳入字段变更
        }

        for (FieldInfo oldField : oldFields) {
            if (!newMap.containsKey(oldField.getName())) {
                removed.add(oldField);
            }
        }

        List<FieldChange> renamed = pairRenames(removed, added);
        return mergeInOrder(newFields, renamed, modified, added, removed);
    }

    /**
     * 将删除+新增配对为字段重命名。
     * 优先：说明相同且类型相同；其次：说明相同但类型不同（重命名+改类型）。
     */
    private List<FieldChange> pairRenames(List<FieldInfo> removed, List<FieldInfo> added) {
        List<FieldChange> renames = new ArrayList<>();
        Set<FieldInfo> matchedRemoved = new LinkedHashSet<>();
        Set<FieldInfo> matchedAdded = new LinkedHashSet<>();

        for (FieldInfo oldField : removed) {
            FieldInfo pair = findRenamePair(oldField, added, matchedAdded, true);
            if (pair == null) {
                pair = findRenamePair(oldField, added, matchedAdded, false);
            }
            if (pair != null) {
                renames.add(FieldChange.renamed(oldField, pair));
                matchedRemoved.add(oldField);
                matchedAdded.add(pair);
            }
        }

        removed.removeIf(matchedRemoved::contains);
        added.removeIf(matchedAdded::contains);
        return renames;
    }

    private FieldInfo findRenamePair(FieldInfo removed, List<FieldInfo> added,
                                     Set<FieldInfo> excluded, boolean requireSameType) {
        for (FieldInfo candidate : added) {
            if (excluded.contains(candidate)) {
                continue;
            }
            if (!descriptionsMatch(removed, candidate)) {
                continue;
            }
            if (requireSameType && !removed.getType().equals(candidate.getType())) {
                continue;
            }
            if (!requireSameType && removed.getType().equals(candidate.getType())) {
                continue;
            }
            return candidate;
        }
        return null;
    }

    /** 说明相同（非空）或双方均为空时视为匹配 */
    private boolean descriptionsMatch(FieldInfo oldField, FieldInfo newField) {
        String oldDesc = normalizeDescription(oldField.getDescription());
        String newDesc = normalizeDescription(newField.getDescription());
        if (oldDesc.isEmpty() && newDesc.isEmpty()) {
            return true;
        }
        if (oldDesc.isEmpty() || newDesc.isEmpty()) {
            return false;
        }
        return oldDesc.equals(newDesc);
    }

    private String normalizeDescription(String description) {
        return description == null ? "" : description.trim();
    }

    /** 按新字段声明顺序合并各变更类型 */
    private List<FieldChange> mergeInOrder(List<FieldInfo> newFields, List<FieldChange> renamed,
                                           List<FieldChange> modified, List<FieldInfo> added,
                                           List<FieldInfo> removed) {
        Map<String, FieldChange> renamedByNewName = new LinkedHashMap<>();
        for (FieldChange change : renamed) {
            renamedByNewName.put(change.getFieldName(), change);
        }

        Map<String, FieldChange> modifiedByName = new LinkedHashMap<>();
        for (FieldChange change : modified) {
            modifiedByName.put(change.getFieldName(), change);
        }

        Set<String> emitted = new LinkedHashSet<>();
        List<FieldChange> result = new ArrayList<>();

        for (FieldInfo newField : newFields) {
            String name = newField.getName();
            if (renamedByNewName.containsKey(name)) {
                result.add(renamedByNewName.get(name));
                emitted.add(name);
            } else if (modifiedByName.containsKey(name)) {
                result.add(modifiedByName.get(name));
                emitted.add(name);
            } else if (added.stream().anyMatch(f -> f.getName().equals(name))) {
                result.add(FieldChange.added(newField));
                emitted.add(name);
            }
        }

        for (FieldInfo oldField : removed) {
            result.add(FieldChange.removed(oldField));
        }
        return result;
    }

    /** 字段列表转 LinkedHashMap，保持声明顺序 */
    private Map<String, FieldInfo> toMap(List<FieldInfo> fields) {
        Map<String, FieldInfo> map = new LinkedHashMap<>();
        for (FieldInfo field : fields) {
            map.put(field.getName(), field);
        }
        return map;
    }

    /** 构造类型变化描述，如 Integer → String */
    private String buildTypeDetail(FieldInfo oldField, FieldInfo newField) {
        if (oldField.getType().equals(newField.getType())) {
            return "";
        }
        return oldField.getType() + " → " + newField.getType();
    }
}
