package top.r3944realms.docchecktoolrefactored;

import javafx.stage.DirectoryChooser;
import javafx.stage.FileChooser;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import top.r3944realms.docchecktoolrefactored.core.Setting;
import top.r3944realms.docchecktoolrefactored.ui.module.ProjectInfoPaneController;
import top.r3944realms.docchecktoolrefactored.util.LoggerMarker;

import java.io.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Properties;
import java.util.concurrent.locks.ReentrantReadWriteLock;

@Slf4j
public enum System {
    INSTANCE;
    private volatile Setting setting;
    @Getter
    private volatile String version;
    private volatile File lastModifiedFile;
    private static final String CONFIG_FILE_NAME = "config.ini";
    private static final Properties properties = new Properties();
    private static final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
    // 默认值
    private static final long DEFAULT_SINGLE_TIMEOUT = 30;
    private static final long DEFAULT_TOTAL_TIMEOUT = 12600;
    private static final boolean DEFAULT_ENABLE_STEP = false;
    @Getter
    private static final ProjectInfoPaneController.ProjectInfo projectInfo = new ProjectInfoPaneController.ProjectInfo();

    public static void init() {
        loadSettings();
        Runtime.getRuntime().addShutdownHook(new Thread(System::saveSettings));
    }

    /** 加载配置文件 */
    private static void loadSettings() {
        lock.writeLock().lock();
        try {
            Path configPath = getConfigPath();
            if (Files.exists(configPath)) {
                try (InputStream input = new FileInputStream(configPath.toFile())) {
                    properties.load(input);
                    log.debug(LoggerMarker.DEBUG_MARKER, "配置文件加载成功: {}", configPath);
                    INSTANCE.setting = propertiesToSetting(properties);
                } catch (IOException e) {
                    log.error(LoggerMarker.DEBUG_MARKER, "读取配置文件失败: {}, 使用默认配置", e.getMessage());
                    INSTANCE.setting = defaultSetting();
                    settingToProperties(INSTANCE.setting, properties);
                }
            } else {
                INSTANCE.setting = defaultSetting();
                settingToProperties(INSTANCE.setting, properties);
                saveSettings(); // 首次启动保存默认配置
                log.debug(LoggerMarker.DEBUG_MARKER, "配置文件不存在，已创建默认配置: {}", configPath);
            }
        } finally {
            lock.writeLock().unlock();
        }
    }

    /** 保存配置文件 */
    private static void saveSettings() {
        lock.readLock().lock();
        try {
            if (INSTANCE.setting != null) {
                Path configPath = getConfigPath();
                Path configDir = configPath.getParent();
                if (!Files.exists(configDir)) {
                    Files.createDirectories(configDir);
                }

                settingToProperties(INSTANCE.setting, properties);

                try (OutputStream output = new FileOutputStream(configPath.toFile())) {
                    properties.store(new OutputStreamWriter(output, StandardCharsets.UTF_8),
                            "DocCheckTool Configuration");
                    log.debug(LoggerMarker.DEBUG_MARKER, "配置文件保存成功: {}", configPath);
                } catch (IOException e) {
                    log.error(LoggerMarker.DEBUG_MARKER, "保存配置文件失败: {}", e.getMessage());
                }
            }
        } catch (IOException e) {
            log.error(LoggerMarker.DEBUG_MARKER, "创建配置目录失败: {}", e.getMessage());
        } finally {
            lock.readLock().unlock();
        }
    }

    /** 获取配置文件路径 */
    private static Path getConfigPath() {
        try {
            // 获取程序运行目录（Jar 所在目录）
            File jarFile = new File(System.class.getProtectionDomain()
                    .getCodeSource().getLocation().toURI());
            File appDir = jarFile.getParentFile();
            Path configDir = appDir.toPath().resolve("config");
            if (!Files.exists(configDir)) {
                Files.createDirectories(configDir);
            }
            return configDir.resolve(CONFIG_FILE_NAME);
        } catch (Exception e) {
            // 出错 fallback 到当前工作目录
            Path fallbackDir = Paths.get(java.lang.System.getProperty("user.dir"), "config");
            try { Files.createDirectories(fallbackDir); } catch (IOException ignored) {}
            return fallbackDir.resolve(CONFIG_FILE_NAME);
        }
    }

    /** 将Setting对象转换为Properties */
    private static void settingToProperties(Setting setting, Properties props) {
        props.setProperty("scanTimeOutS", String.valueOf(setting.getScanTimeout()));
        props.setProperty("taskTimeOutS", String.valueOf(setting.getTaskTimeout()));
        props.setProperty("enableStep", String.valueOf(setting.isEnableStep()));
    }

    /** 将Properties转换为Setting对象 */
    private static Setting propertiesToSetting(Properties props) {
        Setting s = new Setting();

        try {
            s.setScanTimeout(Long.parseLong(props.getProperty("scanTimeOutS", String.valueOf(DEFAULT_SINGLE_TIMEOUT))));
        } catch (NumberFormatException e) {
            s.setScanTimeout(DEFAULT_SINGLE_TIMEOUT);
            log.error(LoggerMarker.DEBUG_MARKER, "scanTimeOutS格式错误，使用默认值{}", DEFAULT_SINGLE_TIMEOUT);
        }

        try {
            s.setTaskTimeout(Long.parseLong(props.getProperty("taskTimeOutS", String.valueOf(DEFAULT_TOTAL_TIMEOUT))));
        } catch (NumberFormatException e) {
            s.setTaskTimeout(DEFAULT_TOTAL_TIMEOUT);
            log.error(LoggerMarker.DEBUG_MARKER, "taskTimeOutS格式错误，使用默认值{}", DEFAULT_TOTAL_TIMEOUT);
        }
        try {
            s.setEnableStep(Boolean.parseBoolean(props.getProperty("enableStep", String.valueOf(Boolean.FALSE))));
        } catch (Exception e) {
            s.setEnableStep(DEFAULT_ENABLE_STEP);
            log.error(LoggerMarker.DEBUG_MARKER, "enableStep格式错误，使用默认值{}", DEFAULT_TOTAL_TIMEOUT);
        }

        return s;
    }

    /** 获取默认Setting */
    private static Setting defaultSetting() {
        Setting s = new Setting();
        s.setScanTimeout(DEFAULT_SINGLE_TIMEOUT);
        s.setTaskTimeout(DEFAULT_TOTAL_TIMEOUT);
        return s;
    }

    /** 手动保存 */
    public static void saveSettingsNow() {
        saveSettings();
    }

    /** 重新加载 */
    public static void reloadSettings() {
        loadSettings();
    }

    /** 获取Setting对象 */
    public static Setting getSetting() {
        return INSTANCE.setting;
    }
    /** 获取File 对象 */
    public static File getlastModifiedFile() {
        return INSTANCE.lastModifiedFile;
    }

    /** 获取配置目录路径 */
    public static String getConfigDirectory() {
        return getConfigPath().getParent().toString();
    }

    /** 获取配置文件路径 */
    public static String getConfigFilePath() {
        return getConfigPath().toString();
    }
    public static void setLastModifiedFile(File lastModifiedFile) {
        INSTANCE.lastModifiedFile = lastModifiedFile;
    }
    public static FileChooser getFileChooser() {
        FileChooser fileChooser = new FileChooser();
        File lastFile = getlastModifiedFile();
        if (lastFile != null) {
            File parentDir = lastFile.getParentFile();
            if (parentDir != null && parentDir.exists() && parentDir.isDirectory()) {
                fileChooser.setInitialDirectory(parentDir);
            }
        }
        return fileChooser;
    }
    public static DirectoryChooser getDirectoryChooser() {
        DirectoryChooser directoryChooser = new DirectoryChooser();
        File lastFile = getlastModifiedFile();
        if (lastFile != null) {
            File parentDir = lastFile.getParentFile();
            if (parentDir != null && parentDir.exists() && parentDir.isDirectory()) {
                directoryChooser.setInitialDirectory(parentDir);
            }
        }
        return directoryChooser;
    }

    /**
     * @return CPU核心数
     */
    public static Integer getAvailableProcessors() {
        return Runtime.getRuntime().availableProcessors();
    }
    public static String version() {
        return System.INSTANCE.getVersion();
    }
    public static void setVersion(String version) {
        System.INSTANCE.version = version;
    }
}
