package net.viper.status.modules.AutoMessage;

import net.md_5.bungee.api.ChatColor;
import net.md_5.bungee.api.CommandSender;
import net.md_5.bungee.api.ProxyServer;
import net.md_5.bungee.api.chat.BaseComponent;
import net.md_5.bungee.api.chat.TextComponent;
import net.md_5.bungee.api.plugin.Command;
import net.md_5.bungee.api.plugin.Plugin;
import net.viper.status.StatusAPI;
import net.viper.status.module.Module;

import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * AutoMessageModule
 *
 * Fix #5:
 *   - Nachrichten werden bei jedem Zyklus frisch aus der Datei gelesen,
 *     damit \u00c4nderungen an messages.txt sofort wirken ohne Neustart.
 *   - Neuer Befehl /automessage reload (Permission: statusapi.automessage)
 *     l\u00e4dt die Konfiguration neu und setzt den Z\u00e4hler zur\u00fcck.
 *   - TextComponent.fromLegacy() → ChatColor.translateAlternateColorCodes f\u00fcr §-Codes.
 */
public class AutoMessageModule implements Module {

    private int taskId = -1;
    private StatusAPI api;
    private final AtomicInteger currentIndex = new AtomicInteger(0);

    // Konfiguration (f\u00fcr Reload zug\u00e4nglich)
    private volatile boolean enabled = false;
    private volatile int intervalSeconds = 300;
    private volatile String fileName = "messages.txt";
    private volatile String prefix = "";

    @Override
    public String getName() { return "AutoMessage"; }

    @Override
    public void onEnable(Plugin plugin) {
        this.api = (StatusAPI) plugin;
        loadSettings();
        ensureMessagesFileExists();

        if (!enabled) return;

        registerReloadCommand();
        scheduleTask();
    }

    @Override
    public void onDisable(Plugin plugin) {
        cancelTask();
    }

    private void ensureMessagesFileExists() {
        File dataFolder = api.getDataFolder();
        if (!dataFolder.exists()) dataFolder.mkdirs();

        File target = new File(dataFolder, fileName);
        if (target.exists()) return;

        // Datei aus den Plugin-Ressourcen kopieren
        try (java.io.InputStream in = api.getResourceAsStream(fileName)) {
            if (in != null) {
                Files.copy(in, target.toPath());
                api.getLogger().info("[AutoMessage] " + fileName + " wurde aus den Ressourcen erstellt.");
                return;
            }
        } catch (IOException e) {
            api.getLogger().warning("[AutoMessage] Konnte " + fileName + " nicht aus Ressourcen kopieren: " + e.getMessage());
        }

        // Fallback: leere Datei mit Hinweis anlegen
        try {
            Files.write(target.toPath(),
                ("# AutoMessage – eine Nachricht pro Zeile\n" +
                 "# Farben mit & oder §-Codes, z.B. &aGr\u00fcner Text\n" +
                 "# Kommentarzeilen (# ...) und Leerzeilen werden ignoriert\n").getBytes(StandardCharsets.UTF_8));
            api.getLogger().info("[AutoMessage] " + fileName + " wurde als leere Vorlage erstellt.");
        } catch (IOException e) {
            api.getLogger().severe("[AutoMessage] Konnte " + fileName + " nicht erstellen: " + e.getMessage());
        }
    }

    private void loadSettings() {
        Properties props = api.getVerifyProperties();
        enabled         = Boolean.parseBoolean(props.getProperty("automessage.enabled", "false"));
        String rawInterval = props.getProperty("automessage.interval", "300");
        try { intervalSeconds = Integer.parseInt(rawInterval); }
        catch (NumberFormatException e) { api.getLogger().warning("Ung\u00fcltiges Intervall f\u00fcr AutoMessage! Nutze Standard (300s)."); intervalSeconds = 300; }
        fileName = props.getProperty("automessage.file", "messages.txt");
        prefix   = props.getProperty("automessage.prefix", "");
    }

    private void registerReloadCommand() {
        ProxyServer.getInstance().getPluginManager().registerCommand(api, new Command("automessage", "statusapi.automessage") {
            @Override
            public void execute(CommandSender sender, String[] args) {
                if (args.length > 0 && "reload".equalsIgnoreCase(args[0])) {
                    cancelTask();
                    loadSettings();
                    currentIndex.set(0);
                    if (enabled) {
                        scheduleTask();
                        sender.sendMessage(ChatColor.GREEN + "[AutoMessage] Neu geladen. Intervall: " + intervalSeconds + "s");
                    } else {
                        sender.sendMessage(ChatColor.YELLOW + "[AutoMessage] Modul ist deaktiviert (automessage.enabled=false).");
                    }
                } else {
                    sender.sendMessage(ChatColor.YELLOW + "/automessage reload");
                }
            }
        });
    }

    private void scheduleTask() {
        taskId = ProxyServer.getInstance().getScheduler().schedule(api, () -> {
            File messageFile = new File(api.getDataFolder(), fileName);
            if (!messageFile.exists()) {
                api.getLogger().warning("[AutoMessage] Datei nicht gefunden: " + messageFile.getAbsolutePath());
                return;
            }

            // Fix #5: Datei bei jedem Tick neu einlesen → \u00c4nderungen wirken sofort
            List<String> messages;
            try {
                messages = Files.readAllLines(messageFile.toPath(), StandardCharsets.UTF_8);
            } catch (IOException e) {
                api.getLogger().severe("[AutoMessage] Fehler beim Lesen von '" + fileName + "': " + e.getMessage());
                return;
            }
            messages.removeIf(line -> line.trim().isEmpty() || line.trim().startsWith("#"));
            if (messages.isEmpty()) return;

            // Index wrappen (threadsafe)
            int idx = currentIndex.getAndUpdate(i -> (i + 1) % messages.size());
            if (idx >= messages.size()) idx = 0;

            String raw = messages.get(idx);
            String prefixPart = prefix.isEmpty() ? "" : ChatColor.translateAlternateColorCodes('&', applyGradients(prefix)) + " ";
            // Gradient zuerst auflösen, dann §/&-Codes übersetzen
            String normalized = raw.replace("\u00a7", "&").replace("§", "&");
            String text = prefixPart + ChatColor.translateAlternateColorCodes('&', applyGradients(normalized));

            BaseComponent[] components = TextComponent.fromLegacyText(text);
            ProxyServer.getInstance().broadcast(components);
        }, intervalSeconds, intervalSeconds, TimeUnit.SECONDS).getId();
    }

    private void cancelTask() {
        if (taskId != -1) {
            ProxyServer.getInstance().getScheduler().cancel(taskId);
            taskId = -1;
        }
    }

    // ── Gradient-Support (%gradient:FARBE1:FARBE2:...:TEXT%) ─────────────────

    private String applyGradients(String input) {
        if (input == null || !input.contains("%gradient:")) return input;
        StringBuilder result = new StringBuilder();
        int i = 0;
        while (i < input.length()) {
            int start = input.indexOf("%gradient:", i);
            if (start < 0) { result.append(input.substring(i)); break; }
            result.append(input, i, start);
            int end = input.indexOf("%", start + 10);
            if (end < 0) { result.append(input.substring(start)); break; }
            String inner = input.substring(start + 10, end);
            List<int[]> stops = new ArrayList<>();
            int colonIdx = 0;
            while (colonIdx < inner.length()) {
                int nextColon = inner.indexOf(':', colonIdx);
                if (nextColon < 0) break;
                String candidate = inner.substring(colonIdx, nextColon);
                int[] rgb = parseGradientColor(candidate);
                if (rgb != null) { stops.add(rgb); colonIdx = nextColon + 1; }
                else break;
            }
            if (stops.size() < 2) { result.append(input, start, end + 1); i = end + 1; continue; }
            String text  = inner.substring(colonIdx);
            String plain = ChatColor.stripColor(ChatColor.translateAlternateColorCodes('&', text));
            boolean bold      = text.contains("&l") || text.contains("\u00A7l");
            boolean italic    = text.contains("&o") || text.contains("\u00A7o");
            boolean underline = text.contains("&n") || text.contains("\u00A7n");
            boolean strike    = text.contains("&m") || text.contains("\u00A7m");
            String fmt = (bold ? "\u00A7l" : "") + (italic ? "\u00A7o" : "")
                       + (underline ? "\u00A7n" : "") + (strike ? "\u00A7m" : "");
            int visLen = 0;
            for (char ch : plain.toCharArray()) if (ch != ' ') visLen++;
            if (visLen == 0) visLen = 1;
            int charIdx = 0;
            int[] lastRgb = stops.get(0);
            for (char ch : plain.toCharArray()) {
                if (ch == ' ') {
                    result.append('\u00A7').append('x');
                    result.append('\u00A7').append(String.format("%02X", lastRgb[0]).charAt(0));
                    result.append('\u00A7').append(String.format("%02X", lastRgb[0]).charAt(1));
                    result.append('\u00A7').append(String.format("%02X", lastRgb[1]).charAt(0));
                    result.append('\u00A7').append(String.format("%02X", lastRgb[1]).charAt(1));
                    result.append('\u00A7').append(String.format("%02X", lastRgb[2]).charAt(0));
                    result.append('\u00A7').append(String.format("%02X", lastRgb[2]).charAt(1));
                    result.append(fmt).append(ch);
                    continue;
                }
                float pos = visLen <= 1 ? 0f : (float) charIdx / (visLen - 1);
                lastRgb = interpolateGradient(stops, pos);
                result.append('\u00A7').append('x');
                result.append('\u00A7').append(String.format("%02X", lastRgb[0]).charAt(0));
                result.append('\u00A7').append(String.format("%02X", lastRgb[0]).charAt(1));
                result.append('\u00A7').append(String.format("%02X", lastRgb[1]).charAt(0));
                result.append('\u00A7').append(String.format("%02X", lastRgb[1]).charAt(1));
                result.append('\u00A7').append(String.format("%02X", lastRgb[2]).charAt(0));
                result.append('\u00A7').append(String.format("%02X", lastRgb[2]).charAt(1));
                result.append(fmt).append(ch);
                charIdx++;
            }
            // §r nach dem Gradient-Block, damit nachfolgende §8/§7 etc. wieder greifen
            result.append('\u00A7').append('r');
            i = end + 1;
        }
        return result.toString();
    }

    private int[] parseGradientColor(String s) {
        s = s.trim();
        if (s.startsWith("&#")) s = s.substring(1);
        if (s.startsWith("#") && s.length() == 7) {
            try {
                return new int[]{
                    Integer.parseInt(s.substring(1,3),16),
                    Integer.parseInt(s.substring(3,5),16),
                    Integer.parseInt(s.substring(5,7),16)
                };
            } catch (Exception ignored) {}
        }
        if (s.startsWith("&") && s.length() == 2) return mcColorToRgb(s.charAt(1));
        return null;
    }

    private int[] interpolateGradient(List<int[]> stops, float pos) {
        if (stops.size() == 1) return stops.get(0);
        float scaled = pos * (stops.size() - 1);
        int i0 = Math.min((int) scaled, stops.size() - 2);
        float t = scaled - i0;
        return new int[]{
            (int)(stops.get(i0)[0] * (1-t) + stops.get(i0+1)[0] * t),
            (int)(stops.get(i0)[1] * (1-t) + stops.get(i0+1)[1] * t),
            (int)(stops.get(i0)[2] * (1-t) + stops.get(i0+1)[2] * t)
        };
    }

    private static int[] mcColorToRgb(char code) {
        switch (Character.toLowerCase(code)) {
            case '0': return new int[]{  0,   0,   0};
            case '1': return new int[]{  0,   0, 170};
            case '2': return new int[]{  0, 170,   0};
            case '3': return new int[]{  0, 170, 170};
            case '4': return new int[]{170,   0,   0};
            case '5': return new int[]{170,   0, 170};
            case '6': return new int[]{255, 170,   0};
            case '7': return new int[]{170, 170, 170};
            case '8': return new int[]{ 85,  85,  85};
            case '9': return new int[]{ 85,  85, 255};
            case 'a': return new int[]{ 85, 255,  85};
            case 'b': return new int[]{ 85, 255, 255};
            case 'c': return new int[]{255,  85,  85};
            case 'd': return new int[]{255,  85, 255};
            case 'e': return new int[]{255, 255,  85};
            case 'f': return new int[]{255, 255, 255};
            default:  return null;
        }
    }
}