package com.codechecker.api.parser;

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;

    public NestedDtoFieldParser(Path repoRoot, List<String> searchDirs) {
        this.sourceLocator = new JavaSourceLocator(repoRoot, searchDirs);
    }

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

    private void collectFields(String className, String prefix, Set<String> visiting,
                               List<NestedFieldInfo> out) throws IOException {
        if (className == null || className.isBlank() || visiting.contains(className)) {
            return;
        }
        visiting.add(className);
        Optional<String> source = sourceLocator.readSourceBySimpleName(className);
        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();
            String simpleType = TypeNameUtils.simpleName(field.getType());
            if (isLeafType(simpleType)) {
                out.add(new NestedFieldInfo(path, field.getType(), field.getDescription()));
            } else {
                out.add(new NestedFieldInfo(path, field.getType(), field.getDescription()));
                collectFields(simpleType, path, visiting, out);
            }
        }
        visiting.remove(className);
    }

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