package de.frigosped.dc.service;

import de.frigosped.dc.model.*;
import jakarta.enterprise.context.ApplicationScoped;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

@ApplicationScoped
public class MarkdownReportBuilder {

    private static final String ICON_OK   = "✅";        // ✅
    private static final String ICON_NOK  = "❌";        // ❌
    private static final String ICON_WARN = "⚠️";  // ⚠️
    private static final String ICON_UNKL = "❓";        // ❓
    private static final String ICON_DOC  = "📄";  // 📄

    private static final DateTimeFormatter DATE_FMT =
            DateTimeFormatter.ofPattern("dd.MM.yyyy HH:mm");

    /**
     * Generates a Markdown report equivalent to DC_UTILS_PKG.generate_report().
     * @param project          project meta (name, notify flags, completedAt)
     * @param documents        ordered list of project documents
     * @param questionsByCatalog map catalogId → sorted questions
     * @param resultsByDoc     map docId → list of saved results
     */
    public String build(OrdsProject project,
                        List<OrdsDocument> documents,
                        Map<Long, List<OrdsQuestion>> questionsByCatalog,
                        Map<Long, List<ResultRequest>> resultsByDoc) {

        boolean showOk   = !"N".equals(project.getNotifyOk());
        boolean showWarn = !"N".equals(project.getNotifyHinweis());
        boolean showDev  = !"N".equals(project.getNotifyAbweichend());

        // Flat lookup: questionId → question
        Map<Long, OrdsQuestion> questionById = new HashMap<>();
        for (List<OrdsQuestion> qs : questionsByCatalog.values()) {
            for (OrdsQuestion q : qs) {
                questionById.put(q.getQuestionId(), q);
            }
        }

        // Global counts (total_all unfiltered, filtered for summary section)
        int totalAll = 0, devCnt = 0, warnCnt = 0, unklarCnt = 0;
        for (List<ResultRequest> list : resultsByDoc.values()) {
            for (ResultRequest r : list) {
                totalAll++;
                if (isDeviation(r))      devCnt++;
                else if (isWarning(r))   warnCnt++;
                else if (isUnklar(r))    unklarCnt++;
            }
        }
        int okCnt = totalAll - devCnt - warnCnt - unklarCnt;

        // Filtered counts matching notify flags (same logic as PL/SQL)
        int filtTotal = (showDev ? devCnt : 0)
                      + (showWarn ? warnCnt : 0)
                      + (showOk ? (okCnt + unklarCnt) : 0);
        int filtDevCnt  = showDev  ? devCnt   : 0;
        int filtWarnCnt = showWarn ? warnCnt  : 0;
        int filtUnklCnt = showOk   ? unklarCnt : 0;
        int filtOkCnt   = filtTotal - filtDevCnt - filtWarnCnt - filtUnklCnt;

        StringBuilder sb = new StringBuilder();

        // Header
        sb.append("# Prüfbericht: ").append(project.getName()).append("\n\n");
        LocalDateTime ts = project.getCompletedAt() != null
                ? project.getCompletedAt() : LocalDateTime.now();
        sb.append("**Abgeschlossen:** ").append(ts.format(DATE_FMT)).append("\n\n");
        sb.append("---\n\n");

        // Summary
        sb.append("## Zusammenfassung\n\n");
        if (showOk) {
            sb.append(ICON_OK).append("  OK: ").append(filtOkCnt).append("\n");
            if (filtUnklCnt > 0) {
                sb.append(ICON_UNKL).append("  Unklar: ").append(filtUnklCnt).append("\n");
            }
        }
        if (showWarn) sb.append(ICON_WARN).append("  Hinweise: ").append(filtWarnCnt).append("\n");
        if (showDev)  sb.append(ICON_NOK).append("  Abweichungen: ").append(filtDevCnt).append("\n");
        sb.append("**Gesamt: ").append(totalAll).append("**\n\n");
        sb.append("---\n\n");

        // Results per document
        sb.append("## Ergebnisse\n\n");

        for (OrdsDocument doc : documents) {
            sb.append("## ").append(ICON_DOC).append(" ").append(doc.getFilename()).append("\n\n");

            List<ResultRequest> docResults = resultsByDoc.getOrDefault(doc.getId(), Collections.emptyList());

            // Per-doc counts
            int dDev = 0, dWarn = 0, dUnkl = 0;
            for (ResultRequest r : docResults) {
                if (isDeviation(r))    dDev++;
                else if (isWarning(r)) dWarn++;
                else if (isUnklar(r))  dUnkl++;
            }
            int dAll = docResults.size();
            int dOk  = dAll - dDev - dWarn - dUnkl;

            if (showOk) {
                sb.append(ICON_OK).append("  OK:              ").append(dOk).append("\n");
                if (dUnkl > 0) sb.append(ICON_UNKL).append("  Unklar:          ").append(dUnkl).append("\n");
            }
            if (showWarn) sb.append(ICON_WARN).append("  Hinweise:        ").append(dWarn).append("\n");
            if (showDev)  sb.append(ICON_NOK).append("  Abweichungen:    ").append(dDev).append("\n");
            sb.append("**Gesamt: ").append(dAll).append("**\n\n");
            sb.append("---\n\n");

            // Results filtered + sorted by category order, then question order
            List<ResultRequest> visible = docResults.stream()
                    .filter(r -> isVisible(r, showOk, showWarn, showDev))
                    .sorted(Comparator
                            .comparingInt((ResultRequest r) -> catOrder(r, questionById))
                            .thenComparingInt(r -> qOrder(r, questionById)))
                    .collect(Collectors.toList());

            Long currentCatId = null;
            for (ResultRequest r : visible) {
                OrdsQuestion q = questionById.get(r.getQuestionId());
                if (q == null) continue;

                if (!Objects.equals(q.getCategoryId(), currentCatId)) {
                    currentCatId = q.getCategoryId();
                    String catName = q.getCategoryName() != null ? q.getCategoryName() : "Allgemein";
                    sb.append("\n### ").append(catName).append("\n\n");
                }

                String icon = icon(r);
                sb.append("\n\n").append(icon).append(" **").append(q.getQuestionText()).append("**\n\n");

                if (r.getAnswer() != null && !r.getAnswer().isBlank()) {
                    sb.append(r.getAnswer()).append("\n\n");
                }
                if (r.getScore() != null) {
                    sb.append("*Score: ").append(r.getScore()).append("%*\n");
                }
                if (r.getTheComment() != null && !r.getTheComment().isBlank()) {
                    sb.append("\n> ").append(r.getTheComment()).append("\n");
                }
                sb.append("\n");
            }

            sb.append("---\n\n");
        }

        return sb.toString();
    }

    /**
     * Generates a Markdown report for a single document.
     */
    public String buildForDocument(OrdsProject project,
                                   OrdsDocument doc,
                                   Map<Long, OrdsQuestion> questionById,
                                   List<ResultRequest> docResults) {

        boolean showOk   = !"N".equals(project.getNotifyOk());
        boolean showWarn = !"N".equals(project.getNotifyHinweis());
        boolean showDev  = !"N".equals(project.getNotifyAbweichend());

        int dDev = 0, dWarn = 0, dUnkl = 0;
        for (ResultRequest r : docResults) {
            if (isDeviation(r))    dDev++;
            else if (isWarning(r)) dWarn++;
            else if (isUnklar(r))  dUnkl++;
        }
        int dAll = docResults.size();
        int dOk  = dAll - dDev - dWarn - dUnkl;

        StringBuilder sb = new StringBuilder();

        sb.append("# Prüfbericht: ").append(project.getName()).append("\n\n");
        LocalDateTime ts = project.getCompletedAt() != null
                ? project.getCompletedAt() : LocalDateTime.now();
        sb.append("**Abgeschlossen:** ").append(ts.format(DATE_FMT)).append("\n\n");
        sb.append("---\n\n");

        sb.append("## ").append(ICON_DOC).append(" ").append(doc.getFilename()).append("\n\n");

        if (showOk) {
            sb.append(ICON_OK).append("  OK: ").append(dOk).append("\n");
            if (dUnkl > 0) sb.append(ICON_UNKL).append("  Unklar: ").append(dUnkl).append("\n");
        }
        if (showWarn) sb.append(ICON_WARN).append("  Hinweise: ").append(dWarn).append("\n");
        if (showDev)  sb.append(ICON_NOK).append("  Abweichungen: ").append(dDev).append("\n");
        sb.append("**Gesamt: ").append(dAll).append("**\n\n");
        sb.append("---\n\n");

        List<ResultRequest> visible = docResults.stream()
                .filter(r -> isVisible(r, showOk, showWarn, showDev))
                .sorted(Comparator
                        .comparingInt((ResultRequest r) -> catOrder(r, questionById))
                        .thenComparingInt(r -> qOrder(r, questionById)))
                .collect(Collectors.toList());

        Long currentCatId = null;
        for (ResultRequest r : visible) {
            OrdsQuestion q = questionById.get(r.getQuestionId());
            if (q == null) continue;

            if (!Objects.equals(q.getCategoryId(), currentCatId)) {
                currentCatId = q.getCategoryId();
                String catName = q.getCategoryName() != null ? q.getCategoryName() : "Allgemein";
                sb.append("\n### ").append(catName).append("\n\n");
            }

            String icon = icon(r);
            sb.append("\n\n").append(icon).append(" **").append(q.getQuestionText()).append("**\n\n");

            if (r.getAnswer() != null && !r.getAnswer().isBlank()) {
                sb.append(r.getAnswer()).append("\n\n");
            }
            if (r.getScore() != null) {
                sb.append("*Score: ").append(r.getScore()).append("%*\n");
            }
            if (r.getTheComment() != null && !r.getTheComment().isBlank()) {
                sb.append("\n> ").append(r.getTheComment()).append("\n");
            }
            sb.append("\n");
        }

        sb.append("---\n\n");
        return sb.toString();
    }

    private boolean isDeviation(ResultRequest r) { return Integer.valueOf(1).equals(r.getDeviation()); }
    private boolean isWarning(ResultRequest r)   { return Integer.valueOf(1).equals(r.getWarning()); }
    private boolean isUnklar(ResultRequest r)    { return "UNKLAR".equals(r.getResultType()) && !isDeviation(r) && !isWarning(r); }

    private boolean isVisible(ResultRequest r, boolean showOk, boolean showWarn, boolean showDev) {
        if (isDeviation(r)) return showDev;
        if (isWarning(r))   return showWarn;
        return showOk;
    }

    private String icon(ResultRequest r) {
        if (isDeviation(r)) return ICON_NOK;
        if (isWarning(r))   return ICON_WARN;
        if (isUnklar(r))    return ICON_UNKL;
        return ICON_OK;
    }

    private int catOrder(ResultRequest r, Map<Long, OrdsQuestion> byId) {
        OrdsQuestion q = byId.get(r.getQuestionId());
        return (q != null && q.getCategoryOrderNr() != null) ? q.getCategoryOrderNr() : Integer.MAX_VALUE;
    }

    private int qOrder(ResultRequest r, Map<Long, OrdsQuestion> byId) {
        OrdsQuestion q = byId.get(r.getQuestionId());
        return (q != null && q.getOrderNr() != null) ? q.getOrderNr() : Integer.MAX_VALUE;
    }
}
