package org.adrianvictor.lib.reflection;

import org.bukkit.Bukkit;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;

public class VersionMatcher {

    public VersionMatcher() {}

    private record Entry(String pattern, String classSuffix) {}

    public String getClassSuffix(String type, String serverVersion) {
        if (type == null || serverVersion == null) {
            return null;
        }

        Map<String, List<Entry>> map = populateMap();
        List<Entry> entries = map.get(type.toLowerCase());
        if (entries == null || entries.isEmpty()) {
            return null;
        }

        // Exact match
        for (Entry e : entries) {
            if (safeMatches(e.pattern, serverVersion)) {
                return e.classSuffix;
            }
        }

        // Fallback to closest version
        List<Entry> sorted = new ArrayList<>(entries);
        Collections.sort(sorted, (a, b) -> compareVersions(a.pattern, b.pattern));

        Entry oldest = sorted.get(0);
        Entry newest = sorted.get(sorted.size() - 1);

        int cmpOldest = compareVersions(serverVersion, oldest.pattern);
        int cmpNewest = compareVersions(serverVersion, newest.pattern);

        if (cmpOldest < 0) {
            return oldest.classSuffix;
        } else if (cmpNewest > 0) {
            return newest.classSuffix;
        }

        return newest.classSuffix;
    }

    public String getServerVersion() {
        String rawVersion = null;
        try {
            rawVersion = Bukkit.getMinecraftVersion();
        } catch (NoSuchMethodError ignored) {}

        if (rawVersion == null || rawVersion.isEmpty()) {
            String v = Bukkit.getVersion();
            int start = v.lastIndexOf("MC: ");
            if (start != -1) {
                rawVersion = v.substring(start + 4, v.length() - 1);
            } else {
                rawVersion = "unknown";
            }
        }

        return rawVersion;
    }

    private Map<String, List<Entry>> populateMap() {
        Map<String, List<Entry>> map = new HashMap<>();
        map.put("release", List.of(
                new Entry("^1\\.21\\..*$", "r1_21")
        ));
        map.put("beta", List.of(
                new Entry("^1\\.7\\.3$", "b1_7_3")
        ));
        return map;
    }

    private int compareVersions(String v1, String v2) {
        String clean1 = v1.replaceAll("[^0-9.]", "");
        String clean2 = v2.replaceAll("[^0-9.]", "");

        String[] a1 = clean1.split("\\.");
        String[] a2 = clean2.split("\\.");

        int len = Math.max(a1.length, a2.length);
        for (int i = 0; i < len; i++) {
            int n1 = i < a1.length ? parseInt(a1[i]) : 0;
            int n2 = i < a2.length ? parseInt(a2[i]) : 0;
            if (n1 != n2) {
                return n1 - n2;
            }
        }
        return 0;
    }

    private int parseInt(String s) {
        try {
            return Integer.parseInt(s);
        } catch (NumberFormatException e) {
            return 0;
        }
    }

    private static boolean safeMatches(String expression, String against) {
        String cleanPattern = expression.trim();
        Pattern pattern = Pattern.compile(cleanPattern, Pattern.CASE_INSENSITIVE);
        return pattern.matcher(against).matches();
    }
}