package com.codechecker.api.parser;

import com.codechecker.git.GitChangeScanner;
import com.codechecker.model.FieldInfo;
import com.codechecker.parser.ClassFieldParser;
import com.codechecker.parser.TypeNameUtils;

import java.io.IOException;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;

/**
 * 递归展开 Dto/Vo 嵌套字段（dot path），与类变更字段解析解耦但复用 ClassFieldParser。
 */
public class NestedDtoFieldParser {
    private static final Set<String> LEAF_TYPES = Set.of(
            "String", "Integer", "int", "Long", "long", "Boolean", "boolean", "Double", "double",
            "Float", "float", "Short", "short", "Byte", "byte", "Character", "char",
            "BigDecimal", "BigInteger", "Date", "LocalDate", "LocalDateTime", "LocalTime",
            "Instant", "Timestamp", "Object", "Void", "void"
    );

    private final ClassFieldParser classFieldParser = new ClassFieldParser();
    private final JavaSourceLocator sourceLocator;
    private final GitChangeScanner gitScanner;
    private final String oldSha;
    private final String newSha;
    private final int maxDepth;

    public NestedDtoFieldParser(Path repoRoot, List<String> searchDirs,
                                GitChangeScanner gitScanner, String oldSha, String newSha, int maxDepth) {
        this.sourceLocator = new JavaSourceLocator(repoRoot, searchDirs);
        this.gitScanner = gitScanner;
        this.oldSha = oldSha;
        this.newSha = newSha;
        this.maxDepth = maxDepth;
    }

    public List<NestedFieldInfo> parseNestedFieldsAtOldCommit(String dtoClassName) throws IOException {
        return parseNestedFields(dtoClassName, oldSha);
    }

    public List<NestedFieldInfo> parseNestedFieldsAtNewCommit(String dtoClassName) throws IOException {
        return parseNestedFields(dtoClassName, newSha);
    }

    private List<NestedFieldInfo> parseNestedFields(String dtoClassName, String sha) throws IOException {
        Set<String> visiting = new HashSet<>();
        List<NestedFieldInfo> result = new ArrayList<>();
        collectFields(dtoClassName, "", visiting, result, sha, 1);
        return result;
    }

    private void collectFields(String className, String prefix, Set<String> visiting,
                               List<NestedFieldInfo> out, String sha, int depth) throws IOException {
        if (className == null || className.isBlank() || visiting.contains(className) || depth > maxDepth) {
            return;
        }
        visiting.add(className);
        Optional<String> source = readSource(className, sha);
        if (source.isEmpty()) {
            visiting.remove(className);
            return;
        }
        List<FieldInfo> fields = classFieldParser.parseFields(source.get(), className);
        for (FieldInfo field : fields) {
            String path = prefix.isBlank() ? field.getName() : prefix + "." + field.getName();
            Set<String> nestedTypes = TypeNameUtils.peelDirectTypeNames(field.getType());
            boolean expanded = false;
            for (String nestedType : nestedTypes) {
                if (isLeafType(nestedType) || nestedType.equals(className)) {
                    continue;
                }
                expanded = true;
                // 嵌套字段路径用类型简单类名（如 UserSelfDto.nickName），不用成员名（userDtos.nickName）
                collectFields(nestedType, nestedType, visiting, out, sha, depth + 1);
            }
            if (!expanded) {
                out.add(new NestedFieldInfo(path, field.getType(), field.getDescription()));
            }
        }
        visiting.remove(className);
    }

    private Optional<String> readSource(String className, String sha) throws IOException {
        if (sha != null && gitScanner != null) {
            return sourceLocator.readSourceAtCommit(gitScanner, sha, className);
        }
        return sourceLocator.readSourceBySimpleName(className);
    }

    private boolean isLeafType(String simpleType) {
        return LEAF_TYPES.contains(simpleType) || simpleType.endsWith("[]");
    }
}
