package net.viper.status.modules.chat;

import java.io.*;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Logger;

/**
 * Verwaltet Spieler-Reports (/report).
 *
 * Reports werden mit einer eindeutigen ID (z.B. RPT-0001) gespeichert und
 * bleiben offen, bis ein Admin sie explizit mit /reportclose <ID> schlie\u00dft.
 *
 * Online-Admins werden sofort benachrichtigt.
 * Offline-Admins erhalten eine verz\u00f6gerte Benachrichtigung beim n\u00e4chsten Login
 * (gesteuert von au\u00dfen via getPendingNotificationFor()).
 *
 * Speicherformat (chat_reports.dat):
 *   id|reporter|reporterUUID|reported|server|messageContext|reason|timestamp|closed|closedBy
 */
public class ReportManager {

    private final File file;
    private final Logger logger;

    /** Alle Reports (offen und geschlossen). */
    private final ConcurrentHashMap<String, ChatReport> reports = new ConcurrentHashMap<>();

    /** Z\u00e4hler f\u00fcr Report-IDs. Wird beim Laden synchronisiert. */
    private final AtomicInteger idCounter = new AtomicInteger(0);

    // ThreadLocal: SimpleDateFormat ist NICHT thread-sicher.
    private static final ThreadLocal<SimpleDateFormat> DATE_FMT =
        ThreadLocal.withInitial(() -> new SimpleDateFormat("dd.MM.yyyy HH:mm:ss"));

    // ===== Report-Datenklasse =====

    public static class ChatReport {
        public String id;
        public String reporterName;
        public UUID   reporterUUID;
        public String reportedName;
        public String server;
        public String messageContext; // letzte bekannte Chatnachricht des Gemeldeten
        public String reason;
        public long   timestamp;
        public boolean closed;
        public String closedBy; // Name des schlie\u00dfenden Admins (oder leer)

        public String getFormattedTime() {
            return DATE_FMT.get().format(new Date(timestamp));
        }
    }

    // ===== Konstruktor =====

    public ReportManager(File dataFolder, Logger logger) {
        this.file   = new File(dataFolder, "chat_reports.dat");
        this.logger = logger;
    }

    // ===== Report-Logik =====

    /**
     * Erstellt einen neuen Report.
     *
     * @param reporterName    Name des meldenden Spielers
     * @param reporterUUID    UUID des meldenden Spielers
     * @param reportedName    Name des gemeldeten Spielers
     * @param server          Server, auf dem sich der Reporter befand
     * @param messageContext  Letzte bekannte Nachricht des Gemeldeten (f\u00fcr Kontext)
     * @param reason          Freitext-Begr\u00fcndung
     * @return die neue Report-ID (z.B. RPT-0001)
     */
    public String createReport(String reporterName, UUID reporterUUID,
                               String reportedName, String server,
                               String messageContext, String reason) {
        String id = String.format("RPT-%04d", idCounter.incrementAndGet());

        ChatReport report     = new ChatReport();
        report.id             = id;
        report.reporterName   = reporterName;
        report.reporterUUID   = reporterUUID;
        report.reportedName   = reportedName;
        report.server         = server;
        report.messageContext = messageContext != null ? messageContext : "";
        report.reason         = reason;
        report.timestamp      = System.currentTimeMillis();
        report.closed         = false;
        report.closedBy       = "";

        reports.put(id, report);
        save();
        return id;
    }

    /**
     * Schlie\u00dft einen Report.
     *
     * @param id        Report-ID (z.B. RPT-0001, case-insensitiv)
     * @param adminName Name des Admins, der den Report schlie\u00dft
     * @return true wenn erfolgreich geschlossen, false wenn nicht gefunden / bereits geschlossen
     */
    public boolean closeReport(String id, String adminName) {
        ChatReport report = getReport(id);
        if (report == null || report.closed) return false;
        report.closed    = true;
        report.closedBy  = adminName;
        save();
        return true;
    }

    /** Gibt einen Report nach ID zur\u00fcck (case-insensitiv). */
    public ChatReport getReport(String id) {
        if (id == null) return null;
        return reports.get(id.toUpperCase());
    }

    /** Gibt alle offenen Reports chronologisch (\u00e4lteste zuerst) zur\u00fcck. */
    public List<ChatReport> getOpenReports() {
        List<ChatReport> list = new ArrayList<>();
        for (ChatReport r : reports.values()) {
            if (!r.closed) list.add(r);
        }
        list.sort(Comparator.comparingLong(r -> r.timestamp));
        return list;
    }

    /** Gibt alle Reports chronologisch zur\u00fcck (auch geschlossene). */
    public List<ChatReport> getAllReports() {
        List<ChatReport> list = new ArrayList<>(reports.values());
        list.sort(Comparator.comparingLong(r -> r.timestamp));
        return list;
    }

    /** Anzahl offener Reports. */
    public int getOpenCount() {
        int count = 0;
        for (ChatReport r : reports.values()) if (!r.closed) count++;
        return count;
    }

    // ===== Persistenz =====

    public void save() {
        try (BufferedWriter bw = new BufferedWriter(
                new OutputStreamWriter(new FileOutputStream(file), "UTF-8"))) {
            for (ChatReport r : reports.values()) {
                bw.write(
                    esc(r.id)             + "|" +
                    esc(r.reporterName)   + "|" +
                    r.reporterUUID        + "|" +
                    esc(r.reportedName)   + "|" +
                    esc(r.server)         + "|" +
                    esc(r.messageContext) + "|" +
                    esc(r.reason)         + "|" +
                    r.timestamp           + "|" +
                    r.closed              + "|" +
                    esc(r.closedBy != null ? r.closedBy : "")
                );
                bw.newLine();
            }
        } catch (IOException e) {
            logger.warning("[ChatModule] Fehler beim Speichern der Reports: " + e.getMessage());
        }
    }

    public void load() {
        reports.clear();
        if (!file.exists()) return;

        int maxNum = 0;
        try (BufferedReader br = new BufferedReader(
                new InputStreamReader(new FileInputStream(file), "UTF-8"))) {
            String line;
            while ((line = br.readLine()) != null) {
                line = line.trim();
                if (line.isEmpty()) continue;
                String[] p = line.split("\\|", -1);
                if (p.length < 10) continue;
                try {
                    ChatReport r      = new ChatReport();
                    r.id              = unesc(p[0]);
                    r.reporterName    = unesc(p[1]);
                    r.reporterUUID    = UUID.fromString(p[2]);
                    r.reportedName    = unesc(p[3]);
                    r.server          = unesc(p[4]);
                    r.messageContext  = unesc(p[5]);
                    r.reason          = unesc(p[6]);
                    r.timestamp       = Long.parseLong(p[7]);
                    r.closed          = Boolean.parseBoolean(p[8]);
                    r.closedBy        = unesc(p[9]);
                    reports.put(r.id.toUpperCase(), r);

                    // Z\u00e4hler auf h\u00f6chste bekannte Nummer synchronisieren
                    if (r.id.toUpperCase().startsWith("RPT-")) {
                        try {
                            int num = Integer.parseInt(r.id.substring(4));
                            if (num > maxNum) maxNum = num;
                        } catch (NumberFormatException ignored) {}
                    }
                } catch (Exception ignored) {}
            }
        } catch (IOException e) {
            logger.warning("[ChatModule] Fehler beim Laden der Reports: " + e.getMessage());
        }
        idCounter.set(maxNum);

    }

    // ===== Escape-Helfer (Pipe-Zeichen und Zeilenumbr\u00fcche escapen) =====

    private static String esc(String s) {
        if (s == null) return "";
        return s.replace("\\", "\\\\")
                .replace("|",  "\\p")
                .replace("\n", "\\n")
                .replace("\r", "");
    }

    private static String unesc(String s) {
        if (s == null) return "";
        return s.replace("\\n",  "\n")
                .replace("\\p",  "|")
                .replace("\\\\", "\\");
    }
}