package stirling.software.SPDF.config;

import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;

import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;

public class ConfigInitializer
        implements ApplicationContextInitializer<ConfigurableApplicationContext> {

    @Override
    public void initialize(ConfigurableApplicationContext applicationContext) {
        try {
            ensureConfigExists();
        } catch (IOException e) {
            throw new RuntimeException("Failed to initialize application configuration", e);
        }
    }

    public void ensureConfigExists() throws IOException {
        // Define the path to the external config directory
        Path destPath = Paths.get("configs", "settings.yml");

        // Check if the file already exists
        if (Files.notExists(destPath)) {
            // Ensure the destination directory exists
            Files.createDirectories(destPath.getParent());

            // Copy the resource from classpath to the external directory
            try (InputStream in =
                    getClass().getClassLoader().getResourceAsStream("settings.yml.template")) {
                if (in != null) {
                    Files.copy(in, destPath);
                } else {
                    throw new FileNotFoundException(
                            "Resource file not found: settings.yml.template");
                }
            }
        } else {
            // If user file exists, we need to merge it with the template from the classpath
            List<String> templateLines;
            try (InputStream in =
                    getClass().getClassLoader().getResourceAsStream("settings.yml.template")) {
                templateLines =
                        new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8))
                                .lines()
                                .collect(Collectors.toList());
            }

            mergeYamlFiles(templateLines, destPath, destPath);
        }
    }

    public void mergeYamlFiles(List<String> templateLines, Path userFilePath, Path outputPath)
            throws IOException {
        List<String> userLines = Files.readAllLines(userFilePath);
        List<String> mergedLines = new ArrayList<>();
        boolean insideAutoGenerated = false;
        boolean beforeFirstKey = true;

        Function<String, Boolean> isCommented = line -> line.trim().startsWith("#");
        Function<String, String> extractKey =
                line -> {
                    String[] parts = line.split(":");
                    return parts.length > 0 ? parts[0].trim().replace("#", "").trim() : "";
                };

        Set<String> userKeys = userLines.stream().map(extractKey).collect(Collectors.toSet());

        for (String line : templateLines) {
            String key = extractKey.apply(line);

            if ("AutomaticallyGenerated:".equalsIgnoreCase(line.trim())) {
                insideAutoGenerated = true;
                mergedLines.add(line);
                continue;
            } else if (insideAutoGenerated && line.trim().isEmpty()) {
                insideAutoGenerated = false;
                mergedLines.add(line);
                continue;
            }

            if (beforeFirstKey && (isCommented.apply(line) || line.trim().isEmpty())) {
                // Handle top comments and empty lines before the first key.
                mergedLines.add(line);
                continue;
            }

            if (!key.isEmpty()) beforeFirstKey = false;

            if (userKeys.contains(key)) {
                // If user has any version (commented or uncommented) of this key, skip the
                // template line
                Optional<String> userValue =
                        userLines.stream()
                                .filter(
                                        l ->
                                                extractKey.apply(l).equalsIgnoreCase(key)
                                                        && !isCommented.apply(l))
                                .findFirst();
                if (userValue.isPresent()) mergedLines.add(userValue.get());
                continue;
            }

            if (isCommented.apply(line) || line.trim().isEmpty() || !userKeys.contains(key)) {
                mergedLines.add(
                        line); // If line is commented, empty or key not present in user's file,
                // retain the
                // template line
                continue;
            }
        }

        // Add any additional uncommented user lines that are not present in the
        // template
        for (String userLine : userLines) {
            String userKey = extractKey.apply(userLine);
            boolean isPresentInTemplate =
                    templateLines.stream()
                            .map(extractKey)
                            .anyMatch(templateKey -> templateKey.equalsIgnoreCase(userKey));
            if (!isPresentInTemplate && !isCommented.apply(userLine)) {
                mergedLines.add(userLine);
            }
        }

        Files.write(outputPath, mergedLines, StandardCharsets.UTF_8);
    }
}
