package eu.mrneznamy.mrultimatelibrary.config;

import java.io.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Utility for modifying YAML files (renaming, setting, adding keys)
 * while perfectly preserving comments (# popisky), blank lines, and original formatting.
 */
public class MrLibYamlUpdater {

    private static final Pattern KEY_PATTERN = Pattern.compile("^(\\s*)([\"']?)([a-zA-Z0-9_.-]+)\\2\\s*:(.*)$");

    /**
     * Renames a key path (e.g. "Section.OldKey") to a new key name ("NewKey")
     * while retaining the original value, spacing, and trailing comments.
     */
    public static boolean renameKey(File file, String oldPath, String newKeyName) {
        if (file == null || !file.exists()) return false;
        try {
            List<String> lines = Files.readAllLines(file.toPath(), StandardCharsets.UTF_8);
            List<String> newLines = new ArrayList<>();
            List<PathSegment> pathStack = new ArrayList<>();
            boolean modified = false;

            for (String line : lines) {
                String trimmed = line.trim();
                if (trimmed.isEmpty() || trimmed.startsWith("#")) {
                    newLines.add(line);
                    continue;
                }

                Matcher matcher = KEY_PATTERN.matcher(line);
                if (matcher.find()) {
                    int indent = matcher.group(1).length();
                    String key = matcher.group(3);
                    String rest = matcher.group(4);

                    pathStack.removeIf(segment -> segment.indent >= indent);
                    pathStack.add(new PathSegment(key, indent));

                    String currentPath = getFullPath(pathStack);
                    if (currentPath.equals(oldPath)) {
                        String quotes = matcher.group(2);
                        String renamedLine = matcher.group(1) + quotes + newKeyName + quotes + ":" + rest;
                        newLines.add(renamedLine);
                        modified = true;

                        pathStack.get(pathStack.size() - 1).key = newKeyName;
                        continue;
                    }
                }
                newLines.add(line);
            }

            if (modified) {
                Files.write(file.toPath(), newLines, StandardCharsets.UTF_8);
                return true;
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return false;
    }

    /**
     * Updates or sets the value of an existing key path (e.g. "Section.Key")
     * while preserving the original spacing and trailing comments.
     */
    public static boolean setKeyValue(File file, String targetPath, Object value) {
        if (file == null || !file.exists()) return false;
        try {
            List<String> lines = Files.readAllLines(file.toPath(), StandardCharsets.UTF_8);
            List<String> newLines = new ArrayList<>();
            List<PathSegment> pathStack = new ArrayList<>();
            boolean modified = false;
            String formattedValue = formatValue(value);

            for (String line : lines) {
                String trimmed = line.trim();
                if (trimmed.isEmpty() || trimmed.startsWith("#")) {
                    newLines.add(line);
                    continue;
                }

                Matcher matcher = KEY_PATTERN.matcher(line);
                if (matcher.find()) {
                    int indent = matcher.group(1).length();
                    String key = matcher.group(3);
                    String rest = matcher.group(4);

                    pathStack.removeIf(segment -> segment.indent >= indent);
                    pathStack.add(new PathSegment(key, indent));

                    String currentPath = getFullPath(pathStack);
                    if (currentPath.equals(targetPath)) {
                        String comment = "";
                        int hashIndex = rest.indexOf('#');
                        if (hashIndex != -1) {
                            comment = " " + rest.substring(hashIndex);
                        }

                        String updatedLine = matcher.group(1) + matcher.group(2) + key + matcher.group(2) + ": " + formattedValue + comment;
                        newLines.add(updatedLine);
                        modified = true;
                        continue;
                    }
                }
                newLines.add(line);
            }

            if (modified) {
                Files.write(file.toPath(), newLines, StandardCharsets.UTF_8);
                return true;
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return false;
    }

    /**
     * Inserts a new key-value pair under a specific parent section path
     * using the correct section indentation, while leaving everything else intact.
     */
    public static boolean addKey(File file, String parentPath, String newKey, Object value) {
        if (file == null || !file.exists()) return false;
        try {
            List<String> lines = Files.readAllLines(file.toPath(), StandardCharsets.UTF_8);
            List<String> newLines = new ArrayList<>();
            List<PathSegment> pathStack = new ArrayList<>();

            int insertIndex = -1;
            int parentIndent = -1;

            for (int i = 0; i < lines.size(); i++) {
                String line = lines.get(i);
                String trimmed = line.trim();
                if (trimmed.isEmpty() || trimmed.startsWith("#")) {
                    continue;
                }

                Matcher matcher = KEY_PATTERN.matcher(line);
                if (matcher.find()) {
                    int indent = matcher.group(1).length();
                    String key = matcher.group(3);

                    pathStack.removeIf(segment -> segment.indent >= indent);
                    pathStack.add(new PathSegment(key, indent));

                    String currentPath = getFullPath(pathStack);
                    if (currentPath.equals(parentPath)) {
                        parentIndent = indent;
                        insertIndex = i;
                    }
                }
            }

            if (insertIndex != -1) {
                int endOfSection = insertIndex + 1;
                for (int i = insertIndex + 1; i < lines.size(); i++) {
                    String line = lines.get(i);
                    String trimmed = line.trim();
                    if (trimmed.isEmpty() || trimmed.startsWith("#")) {
                        continue;
                    }
                    Matcher matcher = KEY_PATTERN.matcher(line);
                    if (matcher.find()) {
                        int indent = matcher.group(1).length();
                        if (indent <= parentIndent) {
                            break;
                        }
                        endOfSection = i + 1;
                    }
                }

                boolean exists = false;
                pathStack.clear();
                for (int i = insertIndex + 1; i < endOfSection; i++) {
                    String line = lines.get(i);
                    Matcher matcher = KEY_PATTERN.matcher(line);
                    if (matcher.find()) {
                        int indent = matcher.group(1).length();
                        String key = matcher.group(3);
                        pathStack.removeIf(segment -> segment.indent >= indent);
                        pathStack.add(new PathSegment(key, indent));

                        String fullPath = parentPath + "." + getFullPath(pathStack);
                        if (fullPath.equals(parentPath + "." + newKey)) {
                            exists = true;
                            break;
                        }
                    }
                }

                if (!exists) {
                    StringBuilder indentStr = new StringBuilder();
                    for (int k = 0; k < parentIndent + 2; k++) {
                        indentStr.append(" ");
                    }
                    String newLineStr = indentStr.toString() + newKey + ": " + formatValue(value);

                    for (int i = 0; i < lines.size(); i++) {
                        newLines.add(lines.get(i));
                        if (i == endOfSection - 1) {
                            newLines.add(newLineStr);
                        }
                    }
                    Files.write(file.toPath(), newLines, StandardCharsets.UTF_8);
                    return true;
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return false;
    }

    private static String getFullPath(List<PathSegment> pathStack) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < pathStack.size(); i++) {
            sb.append(pathStack.get(i).key);
            if (i < pathStack.size() - 1) {
                sb.append(".");
            }
        }
        return sb.toString();
    }

    private static String formatValue(Object value) {
        if (value == null) return "";
        if (value instanceof String) {
            String str = (String) value;
            if (str.contains(" ") || str.contains("&") || str.contains(":") || str.contains("#")) {
                if (!str.startsWith("\"") && !str.startsWith("'")) {
                    return "\"" + str.replace("\"", "\\\"") + "\"";
                }
            }
            return str;
        }
        if (value instanceof List) {
            List<?> list = (List<?>) value;
            StringBuilder sb = new StringBuilder();
            sb.append("[");
            for (int i = 0; i < list.size(); i++) {
                sb.append(formatValue(list.get(i)));
                if (i < list.size() - 1) sb.append(", ");
            }
            sb.append("]");
            return sb.toString();
        }
        return value.toString();
    }

    private static class PathSegment {
        String key;
        int indent;

        PathSegment(String key, int indent) {
            this.key = key;
            this.indent = indent;
        }
    }
}
