package dev.crucifixbeta.core.impl;

import com.google.common.base.Splitter;
import dev.crucifixbeta.CrucifixBeta;
import dev.crucifixbeta.core.Manager;
import dev.crucifixbeta.mod.modules.Module;
import dev.crucifixbeta.mod.modules.impl.client.Fonts;
import dev.crucifixbeta.mod.modules.settings.Setting;
import dev.crucifixbeta.mod.modules.settings.impl.BindSetting;
import dev.crucifixbeta.mod.modules.settings.impl.BooleanSetting;
import dev.crucifixbeta.mod.modules.settings.impl.ColorSetting;
import dev.crucifixbeta.mod.modules.settings.impl.EnumSetting;
import dev.crucifixbeta.mod.modules.settings.impl.SliderSetting;
import dev.crucifixbeta.mod.modules.settings.impl.StringSetting;
import java.awt.Color;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.regex.Pattern;
import org.apache.commons.io.IOUtils;

public class ConfigManager extends Manager {
    public static File options = getFile("options.txt");
    private final Hashtable<String, String> settings = new Hashtable<>();

    public void load() {
        settings.clear();
        read();
        for (Module module : CrucifixBeta.MODULE.getModules()) {
            for (Setting setting : module.getSettings()) {
                String key = module.getName() + "_" + setting.getName();
                if (setting instanceof BooleanSetting value) {
                    value.setValueWithoutTask(getBoolean(key, value.getDefaultValue()));
                } else if (setting instanceof SliderSetting value) {
                    value.setValue(getFloat(key, (float) value.getDefaultValue()));
                } else if (setting instanceof BindSetting value) {
                    value.setValue(getInt(key, value.getDefaultValue()));
                    value.setHoldEnable(getBoolean(key + "_hold"));
                } else if (setting instanceof EnumSetting<?> value) {
                    value.loadSetting(getString(key));
                } else if (setting instanceof ColorSetting value) {
                    value.setValue(new Color(getInt(key, value.getDefaultValue().getRGB()), true));
                    value.setRainbow(getBoolean(key + "Rainbow", value.getDefaultRainbow()));
                    if (value.injectBoolean) {
                        value.booleanValue = getBoolean(key + "Boolean", value.getDefaultBooleanValue());
                    }
                } else if (setting instanceof StringSetting value) {
                    value.setValue(getString(key, value.getDefaultValue()));
                }
            }
            module.setState(getBoolean(module.getName() + "_state", isLockedClientModule(module)));
        }
    }

    public void read() {
        if (!options.exists()) {
            return;
        }
        try {
            Splitter splitter = Splitter.on(':').limit(2);
            for (String line : IOUtils.readLines(new FileInputStream(options), StandardCharsets.UTF_8)) {
                try {
                    Iterator<String> parts = splitter.split(line).iterator();
                    settings.put(parts.next(), parts.next());
                } catch (Exception ignored) {
                    System.out.println("Skipping bad option: " + line);
                }
            }
        } catch (Exception exception) {
            exception.printStackTrace();
            System.out.println("[" + CrucifixBeta.NAME + "] Failed to load settings");
        }
    }

    private static boolean isLockedClientModule(Module module) {
        return module instanceof Fonts;
    }

    public void save() {
        writeConfig(options, false);
    }

    private static void writeConfig(File file, boolean defaults) {
        if (file == null || CrucifixBeta.MODULE == null) {
            return;
        }
        File parent = file.getParentFile();
        if (parent != null) {
            parent.mkdirs();
        }
        PrintWriter writer = null;
        try {
            writer = new PrintWriter(new OutputStreamWriter(new FileOutputStream(file), StandardCharsets.UTF_8));
            for (Module module : CrucifixBeta.MODULE.getModules()) {
                for (Setting setting : module.getSettings()) {
                    String key = module.getName() + "_" + setting.getName();
                    if (setting instanceof BooleanSetting value) {
                        writer.println(key + ":" + (defaults ? value.getDefaultValue() : value.getValue()));
                    } else if (setting instanceof SliderSetting value) {
                        writer.println(key + ":" + (defaults ? value.getDefaultValue() : value.getValue()));
                    } else if (setting instanceof BindSetting value) {
                        writer.println(key + ":" + (defaults ? value.getDefaultValue() : value.getValue()));
                        writer.println(key + "_hold:" + (!defaults && value.isHoldEnable()));
                    } else if (setting instanceof EnumSetting<?> value) {
                        writer.println(key + ":" + (defaults ? value.getDefaultValue().name() : value.getValue().name()));
                    } else if (setting instanceof ColorSetting value) {
                        writer.println(key + ":" + (defaults ? value.getDefaultValue().getRGB() : value.getRawValue().getRGB()));
                        writer.println(key + "Rainbow:" + (defaults ? value.getDefaultRainbow() : value.rainbow));
                        if (value.injectBoolean) {
                            writer.println(key + "Boolean:" + (defaults ? value.getDefaultBooleanValue() : value.booleanValue));
                        }
                    } else if (setting instanceof StringSetting value) {
                        writer.println(key + ":" + (defaults ? value.getDefaultValue() : value.getValue()));
                    }
                }
                writer.println(module.getName() + "_state:" + (!defaults && module.isOn() || defaults && isLockedClientModule(module)));
            }
        } catch (Exception exception) {
            exception.printStackTrace();
            System.out.println("[" + CrucifixBeta.NAME + "] Failed to save settings");
        } finally {
            IOUtils.closeQuietly(writer);
        }
    }

    public static File getCfgFolder() {
        File folder = new File(getFolder(), "cfg");
        folder.mkdirs();
        return folder;
    }

    public static File getCfgFile(String name) {
        String clean = sanitizeCfgName(name);
        return clean.isEmpty() ? null : new File(getCfgFolder(), clean + ".cfg");
    }

    public static ArrayList<String> listCfgNames() {
        ArrayList<String> names = new ArrayList<>();
        File[] files = getCfgFolder().listFiles();
        if (files == null) {
            return names;
        }
        for (File file : files) {
            String name = file.getName();
            if (file.isFile() && name.endsWith(".cfg")) {
                names.add(name.substring(0, name.length() - 4));
            }
        }
        names.sort(String::compareToIgnoreCase);
        return names;
    }

    public static String sanitizeCfgName(String name) {
        if (name == null) {
            return "";
        }
        StringBuilder clean = new StringBuilder();
        for (char c : name.trim().toCharArray()) {
            if (Character.isLetterOrDigit(c) || c == '_' || c == '-') {
                clean.append(c);
            } else if (c == ' ') {
                clean.append('_');
            }
        }
        return clean.toString();
    }

    public static String uniqueCfgName(String base) {
        String clean = sanitizeCfgName(base);
        if (clean.isEmpty()) {
            clean = "config";
        }
        String candidate = clean;
        for (int index = 1; getCfgFile(candidate).exists(); index++) {
            candidate = clean + "_" + index;
        }
        return candidate;
    }

    public static void writeDefaultCfg(File file) {
        writeConfig(file, true);
    }

    public static String createDefaultCfg(String nameInput) {
        String name = uniqueCfgName(nameInput);
        writeDefaultCfg(getCfgFile(name));
        return name;
    }

    public static String backupCfg(String fromName, String toNameInput) {
        File source = getCfgFile(fromName);
        if (source == null || !source.isFile()) {
            return null;
        }
        String requested = sanitizeCfgName(toNameInput);
        if (requested.isEmpty()) {
            LocalDate date = LocalDate.now();
            requested = sanitizeCfgName(fromName) + "_" + date.getYear() + "-" + date.getMonthValue() + "-" + date.getDayOfMonth();
        }
        String targetName = uniqueCfgName(requested);
        try {
            Files.copy(source.toPath(), getCfgFile(targetName).toPath(), StandardCopyOption.REPLACE_EXISTING);
            return targetName;
        } catch (Exception exception) {
            exception.printStackTrace();
            return null;
        }
    }

    public static void deleteCfg(String name) {
        File file = getCfgFile(name);
        if (file != null) {
            file.delete();
        }
    }

    public static String saveCfg(String nameInput) {
        String name = sanitizeCfgName(nameInput);
        if (name.isEmpty()) {
            return null;
        }
        options = getCfgFile(name);
        CrucifixBeta.CONFIG.save();
        return name;
    }

    public static boolean loadCfg(String name) {
        File file = getCfgFile(name);
        if (file == null || !file.isFile()) {
            return false;
        }
        options = file;
        CrucifixBeta.CONFIG.load();
        if (Fonts.INSTANCE != null) {
            Fonts.INSTANCE.refresh();
        }
        return true;
    }

    public int getInt(String setting, int defaultValue) {
        String value = settings.get(setting);
        return value == null || !isInteger(value) ? defaultValue : Integer.parseInt(value);
    }

    public float getFloat(String setting, float defaultValue) {
        String value = settings.get(setting);
        return value == null || !isFloat(value) ? defaultValue : Float.parseFloat(value);
    }

    public boolean getBoolean(String setting) {
        return Boolean.parseBoolean(settings.get(setting));
    }

    public boolean getBoolean(String setting, boolean defaultValue) {
        String value = settings.get(setting);
        return value == null ? defaultValue : Boolean.parseBoolean(value);
    }

    public String getString(String setting) {
        return settings.get(setting);
    }

    public String getString(String setting, String defaultValue) {
        return settings.getOrDefault(setting, defaultValue);
    }

    public boolean isInteger(String value) {
        return Pattern.compile("^[-+]?[\\d]*$").matcher(value).matches();
    }

    public boolean isFloat(String value) {
        return value.matches("^[-+]?[0-9]*\\.?[0-9]+([eE][-+]?[0-9]+)?$");
    }
}
