package de.assecutor.votianlt.ai.service;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import de.assecutor.votianlt.model.Job;
import de.assecutor.votianlt.model.JobStatus;
import de.assecutor.votianlt.service.JobStatisticsService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.Month;
import java.time.Year;
import java.time.YearMonth;
import java.time.format.TextStyle;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Map;

/**
 * Service for AI-assisted statistics analysis with chart visualization. Uses LM
 * Studio via direct REST client (like aimailassistant) instead of Spring AI.
 */
@Service
@Slf4j
public class AiStatisticsService {

    private final LlmRestClient llmClient;
    private final JobStatisticsService statisticsService;
    private final ObjectMapper objectMapper;

    public AiStatisticsService(LlmRestClient llmClient, JobStatisticsService statisticsService) {
        this.llmClient = llmClient;
        this.statisticsService = statisticsService;
        this.objectMapper = new ObjectMapper();
        log.info("AiStatisticsService initialized with direct REST client");
    }

    /**
     * Response record containing text and optional chart data.
     */
    public record StatisticsResponse(String textResponse, String chartType, String chartData, String tableHtml) {
    }

    private enum TimeScope {
        ALL_TIME, CURRENT_MONTH, CURRENT_YEAR
    }

    private record LlmVisualizationDecision(String answer, boolean showChart, boolean showTable) {
    }

    private record RevenueFacts(BigDecimal totalRevenue, long jobCount, List<Map.Entry<String, BigDecimal>> topCustomers) {
    }

    /**
     * Analyze a statistics query and return a response with optional visualization.
     */
    public StatisticsResponse analyzeStatisticsQuery(String userQuery, String currentUserId) {
        log.info("Processing statistics query for user {}: {}", currentUserId, userQuery);

        if (currentUserId == null || currentUserId.isBlank()) {
            throw new IllegalArgumentException("currentUserId must not be blank");
        }

        // Determine query type and prepare chart data (includes customer filter
        // detection)
        QueryAnalysis analysis = analyzeQueryType(userQuery, currentUserId);
        log.debug("Query analysis - Type: {}, Chart: {}, Customer: {}, Status: {}", analysis.queryType,
                analysis.chartType, analysis.customerFilter != null ? analysis.customerFilter : "none",
                analysis.statusFilter != null ? analysis.statusFilter : "none");

        // Gather context (statistics or job list depending on query type)
        String statisticsContext = buildStatisticsContext(currentUserId, analysis);

        // Build prompt for LLM
        String prompt = buildPrompt(userQuery, statisticsContext, analysis);

        // System prompt - different for list vs statistics queries
        String systemPrompt = analysis.queryType.equals("list") ? buildListSystemPrompt()
                : buildStatisticsSystemPrompt();

        // Call LLM via direct REST client (like aimailassistant)
        String llmResponse = llmClient.chat(systemPrompt, prompt);

        if (llmResponse != null && !llmResponse.isBlank()) {
            LlmVisualizationDecision decision = parseVisualizationDecision(llmResponse, analysis, currentUserId);
            log.info("LLM response received, length: {} chars, showChart: {}, showTable: {}", llmResponse.length(),
                    decision.showChart(), decision.showTable());

            String chartType = decision.showChart() ? analysis.chartType : null;
            String chartData = decision.showChart() ? analysis.chartData : null;
            String tableHtml = decision.showTable() ? buildTableHtml(currentUserId, analysis) : null;
            String responseText = selectAnswerText(decision.answer(), currentUserId, analysis);
            return new StatisticsResponse(responseText, chartType, chartData, tableHtml);
        } else {
            log.warn("LLM returned null or blank response, using fallback");
            return new StatisticsResponse(buildFallbackResponse(currentUserId, analysis), null, null, null);
        }
    }

    private record QueryAnalysis(String queryType, String chartType, String chartData, boolean tableRequested,
            String customerFilter, JobStatus statusFilter, TimeScope timeScope, boolean revenueBreakdownRequested,
            boolean amountOnlyRequested) {
    }

    private String buildStatisticsSystemPrompt() {
        return """
                Du bist ein Statistik-Assistent für ein Logistikunternehmen.

                DATENSCHUTZ:
                - Nutze ausschließlich den bereitgestellten Datenkontext
                - Der Datenkontext enthält ausschließlich Daten des aktuell angemeldeten Benutzers
                - Untersuche, erwähne oder vermute niemals Daten anderer Benutzer

                WICHTIG - ANTWORTSTIL:
                - Gib ausschließlich ein JSON-Objekt im geforderten Format aus
                - Gib niemals Gedankengänge, Zwischenschritte oder Tags wie <think> aus
                - Wiederhole, paraphrasiere oder fasse die Benutzerfrage nicht zusammen
                - Vermeide Formulierungen wie "Sie fragen", "Du fragst", "Die Frage ist", "Gewünscht ist" oder "Der Benutzer möchte wissen"
                - Im Feld "answer" beginnt die Antwort direkt mit dem Ergebnis
                - Beantworte NUR die gestellte Frage - keine zusätzlichen Informationen!
                - Keine allgemeinen Tipps, Empfehlungen oder weiterführende Hinweise
                - Keine Vergleiche mit anderen Daten, außer explizit gefragt
                - Kurz und präzise: maximal 2-3 Sätze
                - Nenne die relevanten Zahlen direkt
                - Jede genannte Zahl muss exakt aus dem bereitgestellten Kontext stammen

                DIAGRAMM-HINWEIS:
                - Falls ein Diagramm angezeigt wird, erwähne oder beschreibe es nicht
                - Sage NIEMALS "Ich kann kein Diagramm zeichnen" oder ähnliches

                TABELLEN-HINWEIS:
                - Falls eine Tabelle angezeigt wird, erwähne oder beschreibe sie nicht
                - Verwende keine Tabellen im Antworttext

                AUSGABEFORMAT:
                - Antworte IMMER als gültiges JSON-Objekt
                - Format exakt: {"answer":"...","showChart":true|false,"showTable":true|false}
                - "answer" enthält nur den sichtbaren Antworttext für den Benutzer
                - "showChart" ist nur dann true, wenn ein angefordertes Diagramm anhand der Daten sinnvoll ist
                - "showTable" ist nur dann true, wenn eine angeforderte Tabelle anhand der Daten sinnvoll ist
                - Wenn eine Visualisierung nicht sinnvoll ist, setze das Flag auf false und erwähne das nicht im Antworttext
                - Gib ausschließlich das JSON-Objekt aus, keinen zusätzlichen Text

                FORMATIERUNG:
                - Keine Tabellen
                - Keine langen Aufzählungen
                - Fließtext bevorzugen

                Antworte auf Deutsch.
                """;
    }

    private String buildListSystemPrompt() {
        return """
                Du bist ein Assistent für ein Logistikunternehmen.

                DATENSCHUTZ:
                - Nutze ausschließlich den bereitgestellten Datenkontext
                - Der Datenkontext enthält ausschließlich Daten des aktuell angemeldeten Benutzers
                - Untersuche, erwähne oder vermute niemals Daten anderer Benutzer

                WICHTIG - ANTWORTSTIL:
                - Der Benutzer fragt nach einer Liste von Jobs/Aufträgen
                - Gib ausschließlich ein JSON-Objekt im geforderten Format aus
                - Gib niemals Gedankengänge, Zwischenschritte oder Tags wie <think> aus
                - Wiederhole, paraphrasiere oder fasse die Benutzerfrage nicht zusammen
                - Vermeide Formulierungen wie "Sie fragen", "Du fragst", "Die Frage ist", "Gewünscht ist" oder "Der Benutzer möchte wissen"
                - Im Feld "answer" beginnt die Antwort direkt mit dem Ergebnis
                - Nenne nur das Ergebnis der Suche, z.B. die Anzahl der Treffer oder die relevanten Jobnummern
                - Keine Einleitung, keine Meta-Kommentare, keine Wiederholung der Anfrage
                - Maximal 1-2 Sätze
                - Jede genannte Zahl muss exakt aus dem bereitgestellten Kontext stammen

                KEIN DIAGRAMM:
                - Es wird KEIN Diagramm angezeigt bei Listen-Anfragen
                - Erwähne keine Diagramme

                TABELLEN:
                - Falls eine Tabelle angezeigt wird, erwähne oder beschreibe sie nicht
                - Verwende keine Tabellen im Antworttext

                AUSGABEFORMAT:
                - Antworte IMMER als gültiges JSON-Objekt
                - Format exakt: {"answer":"...","showChart":true|false,"showTable":true|false}
                - "answer" enthält nur den sichtbaren Antworttext für den Benutzer
                - "showChart" ist nur dann true, wenn ein angefordertes Diagramm anhand der Daten sinnvoll ist
                - "showTable" ist nur dann true, wenn eine angeforderte Tabelle anhand der Daten sinnvoll ist
                - Wenn eine Visualisierung nicht sinnvoll ist, setze das Flag auf false und erwähne das nicht im Antworttext
                - Gib ausschließlich das JSON-Objekt aus, keinen zusätzlichen Text

                Antworte auf Deutsch.
                """;
    }

    /**
     * Detect user-specified chart type from the query. Returns null if no specific
     * chart type was requested.
     */
    private String detectUserChartTypePreference(String query) {
        String lowerQuery = query.toLowerCase();

        // Balkendiagramm / Bar Chart
        if (lowerQuery.contains("balken") || lowerQuery.contains("bar chart") || lowerQuery.contains("säulen")
                || lowerQuery.contains("balkendiagramm")) {
            return "bar";
        }

        // Tortendiagramm / Pie Chart
        if (lowerQuery.contains("torten") || lowerQuery.contains("pie") || lowerQuery.contains("kreis")
                || lowerQuery.contains("tortendiagramm") || lowerQuery.contains("kreisdiagramm")) {
            return "pie";
        }

        // Donut / Ring Chart
        if (lowerQuery.contains("donut") || lowerQuery.contains("ring") || lowerQuery.contains("doughnut")) {
            return "doughnut";
        }

        // Liniendiagramm / Line Chart
        if (lowerQuery.contains("linie") || lowerQuery.contains("line") || lowerQuery.contains("liniendiagramm")
                || lowerQuery.contains("kurve") || lowerQuery.contains("graph")) {
            return "line";
        }

        // Flächendiagramm / Area Chart
        if (lowerQuery.contains("fläche") || lowerQuery.contains("area") || lowerQuery.contains("flächendiagramm")) {
            return "line"; // Line with fill=true
        }

        // Radar Chart
        if (lowerQuery.contains("radar") || lowerQuery.contains("netz") || lowerQuery.contains("spinne")) {
            return "radar";
        }

        // Polararea Chart
        if (lowerQuery.contains("polar")) {
            return "polarArea";
        }

        return null; // No specific preference
    }

    /**
     * Detect if the user explicitly wants a chart or diagram.
     */
    private boolean isChartRequested(String lowerQuery, String specificChartType) {
        if (specificChartType != null) {
            return true;
        }

        return lowerQuery.contains("diagramm") || lowerQuery.contains("chart") || lowerQuery.contains("grafik")
                || lowerQuery.contains("grafisch") || lowerQuery.contains("visualisier")
                || lowerQuery.contains("visualisierung") || lowerQuery.contains("darstellen")
                || lowerQuery.contains("abbilden");
    }

    /**
     * Detect if the user explicitly wants a table.
     */
    private boolean isTableRequested(String lowerQuery) {
        return lowerQuery.contains("tabelle") || lowerQuery.contains("tabellarisch")
                || lowerQuery.contains("tabellenform") || lowerQuery.contains("als tabelle")
                || lowerQuery.contains("als table") || lowerQuery.contains("table");
    }

    private TimeScope detectTimeScope(String lowerQuery) {
        if (lowerQuery.contains("diesen monat") || lowerQuery.contains("diesem monat")
                || lowerQuery.contains("aktuellen monat") || lowerQuery.contains("monat bisher")) {
            return TimeScope.CURRENT_MONTH;
        }
        if (lowerQuery.contains("dieses jahr") || lowerQuery.contains("diesem jahr")
                || lowerQuery.contains("aktuelles jahr") || lowerQuery.contains("jahr bisher")) {
            return TimeScope.CURRENT_YEAR;
        }
        return TimeScope.ALL_TIME;
    }

    private boolean isRevenueBreakdownRequested(String lowerQuery) {
        return lowerQuery.contains("top kunden") || lowerQuery.contains("kundenumsatz")
                || lowerQuery.contains("umsatz nach kunden") || lowerQuery.contains("umsatz pro kunde")
                || lowerQuery.contains("umsatz je kunde") || lowerQuery.contains("pro kunde")
                || lowerQuery.contains("je kunde");
    }

    private boolean isAmountOnlyRequested(String lowerQuery) {
        return lowerQuery.contains("nur den betrag") || lowerQuery.contains("nur betrag")
                || lowerQuery.contains("nur den wert") || lowerQuery.contains("nur wert")
                || lowerQuery.contains("nur in euro") || lowerQuery.contains("nur euro")
                || lowerQuery.contains("ausschließlich den betrag")
                || lowerQuery.contains("ausschliesslich den betrag");
    }

    private boolean isCountQuery(String lowerQuery) {
        boolean asksForCount = lowerQuery.contains("wie viele") || lowerQuery.contains("wieviele")
                || lowerQuery.contains("anzahl") || lowerQuery.contains("zähle") || lowerQuery.contains("zaehle")
                || lowerQuery.contains("insgesamt");
        boolean asksForDistribution = lowerQuery.contains("status") || lowerQuery.contains("verteilung")
                || lowerQuery.contains("übersicht");
        return asksForCount && !asksForDistribution;
    }

    private QueryAnalysis analyzeQueryType(String query, String currentUserId) {
        String lowerQuery = query.toLowerCase();

        // First, check if this is a LIST query (no chart needed)
        boolean isListQuery = isListQuery(lowerQuery);
        log.debug("Is list query: {}", isListQuery);

        // Check if user specified a chart type (only relevant for non-list queries)
        String userChartType = isListQuery ? null : detectUserChartTypePreference(query);
        log.debug("User chart type preference: {}", userChartType != null ? userChartType : "none");

        boolean chartRequested = !isListQuery && isChartRequested(lowerQuery, userChartType);
        log.debug("Chart requested: {}", chartRequested);
        boolean tableRequested = isTableRequested(lowerQuery);
        log.debug("Table requested: {}", tableRequested);
        TimeScope timeScope = detectTimeScope(lowerQuery);
        log.debug("Time scope: {}", timeScope);
        boolean amountOnlyRequested = isAmountOnlyRequested(lowerQuery);
        log.debug("Amount-only requested: {}", amountOnlyRequested);

        // Check if user specified a customer filter
        String customerFilter = detectCustomerFilter(query, currentUserId);
        log.debug("Customer filter: {}", customerFilter != null ? customerFilter : "none (showing all data)");

        // Check if user specified a status filter
        JobStatus statusFilter = detectStatusFilter(lowerQuery);
        log.debug("Status filter: {}", statusFilter != null ? statusFilter : "none");

        // For list queries, return no chart
        if (isListQuery) {
            return new QueryAnalysis("list", null, null, tableRequested, customerFilter, statusFilter, timeScope,
                    false, amountOnlyRequested);
        }

        // Determine query type and default chart type
        String queryType;
        String defaultChartType;

        // Status-bezogene Anfragen (Statistik, nicht Liste)
        if (isCountQuery(lowerQuery)) {
            queryType = "count";
            defaultChartType = "bar";
        }
        // Status-bezogene Anfragen (Statistik, nicht Liste)
        else if ((lowerQuery.contains("status")
                && (lowerQuery.contains("statistik") || lowerQuery.contains("verteilung")
                        || lowerQuery.contains("übersicht") || lowerQuery.contains("wie viele")
                        || lowerQuery.contains("anzahl")))
                || (lowerQuery.contains("anzahl") && lowerQuery.contains("verteilung"))
                || lowerQuery.contains("zählen")) {
            queryType = "status";
            defaultChartType = "doughnut";
        }
        // Umsatz-bezogene Anfragen
        else if (lowerQuery.contains("umsatz") || lowerQuery.contains("revenue") || lowerQuery.contains("einnahmen")) {
            queryType = "revenue";
            defaultChartType = "bar";
        }
        // Trend-bezogene Anfragen
        else if (lowerQuery.contains("trend") || lowerQuery.contains("monat") || lowerQuery.contains("entwicklung")
                || lowerQuery.contains("jahr") || lowerQuery.contains("verlauf")) {
            queryType = "trend";
            defaultChartType = "line";
        }
        // Task-bezogene Anfragen
        else if (lowerQuery.contains("task") || lowerQuery.contains("aufgabe") || lowerQuery.contains("erledigt")) {
            queryType = "tasks";
            defaultChartType = "doughnut";
        }
        // Allgemeine Übersicht
        else {
            queryType = "overview";
            defaultChartType = "bar";
        }

        boolean revenueBreakdownRequested = "revenue".equals(queryType) && customerFilter == null
                && isRevenueBreakdownRequested(lowerQuery);
        log.debug("Revenue breakdown requested: {}", revenueBreakdownRequested);

        String chartType = null;
        String chartData = null;
        if (chartRequested) {
            // Use user's chart type preference if specified, otherwise use default
            chartType = userChartType != null ? userChartType : defaultChartType;
            chartData = buildChartData(queryType, currentUserId, customerFilter, statusFilter, timeScope,
                    revenueBreakdownRequested);
        }

        return new QueryAnalysis(queryType, chartType, chartData, tableRequested, customerFilter, statusFilter,
                timeScope, revenueBreakdownRequested, amountOnlyRequested);
    }

    private String buildChartData(String queryType, String currentUserId, String customerFilter,
            JobStatus statusFilter, TimeScope timeScope, boolean revenueBreakdownRequested) {
        return switch (queryType) {
        case "count" -> buildCountChartData(currentUserId, customerFilter, statusFilter);
        case "status" -> buildStatusChartData(currentUserId, customerFilter);
        case "revenue" -> buildRevenueChartData(currentUserId, customerFilter, timeScope, revenueBreakdownRequested);
        case "trend" -> buildTrendChartData(currentUserId, customerFilter);
        case "tasks" -> buildTaskChartData(currentUserId);
        default -> buildOverviewChartData(currentUserId, customerFilter);
        };
    }

    private String buildTableHtml(String currentUserId, QueryAnalysis analysis) {
        return switch (analysis.queryType) {
        case "count" -> buildCountTableHtml(currentUserId, analysis.customerFilter, analysis.statusFilter);
        case "list" -> buildJobsTableHtml(currentUserId, analysis.customerFilter, analysis.statusFilter);
        case "status" -> buildStatusTableHtml(currentUserId, analysis.customerFilter);
        case "revenue" -> buildRevenueTableHtml(currentUserId, analysis);
        case "trend" -> buildTrendTableHtml(currentUserId, analysis.customerFilter);
        case "tasks" -> buildTasksTableHtml(currentUserId);
        default -> buildOverviewTableHtml(currentUserId, analysis.customerFilter);
        };
    }

    /**
     * Detect status filter from the query.
     */
    private JobStatus detectStatusFilter(String lowerQuery) {
        if (lowerQuery.contains("angelegt") || lowerQuery.contains("erstellt") || lowerQuery.contains("created")) {
            return JobStatus.CREATED;
        }
        if (lowerQuery.contains("in bearbeitung") || lowerQuery.contains("in progress")) {
            return JobStatus.IN_PROGRESS;
        }
        if (lowerQuery.contains("abholung geplant") || lowerQuery.contains("pickup scheduled")) {
            return JobStatus.PICKUP_SCHEDULED;
        }
        if (lowerQuery.contains("abgeholt") || lowerQuery.contains("picked up")) {
            return JobStatus.PICKED_UP;
        }
        if (lowerQuery.contains("in transport") || lowerQuery.contains("in transit")
                || lowerQuery.contains("unterwegs")) {
            return JobStatus.IN_TRANSIT;
        }
        if (lowerQuery.contains("zugestellt") || lowerQuery.contains("delivered") || lowerQuery.contains("geliefert")) {
            return JobStatus.DELIVERED;
        }
        if (lowerQuery.contains("abgeschlossen") || lowerQuery.contains("completed") || lowerQuery.contains("fertig")) {
            return JobStatus.COMPLETED;
        }
        if (lowerQuery.contains("storniert") || lowerQuery.contains("cancelled")
                || lowerQuery.contains("abgebrochen")) {
            return JobStatus.CANCELLED;
        }
        return null;
    }

    /**
     * Detect if the query is asking for a list of jobs (not statistics).
     */
    private boolean isListQuery(String lowerQuery) {
        // Keywords that indicate a list/detail query (not statistics)
        boolean hasListKeywords = lowerQuery.contains("zeige alle") || lowerQuery.contains("liste")
                || lowerQuery.contains("welche jobs") || lowerQuery.contains("alle jobs")
                || lowerQuery.contains("alle aufträge") || lowerQuery.contains("zeige die jobs")
                || lowerQuery.contains("zeige die aufträge") || lowerQuery.contains("zeig mir die")
                || lowerQuery.contains("gib mir die");

        // Keywords that indicate statistics (override list detection)
        boolean hasStatsKeywords = lowerQuery.contains("statistik") || lowerQuery.contains("diagramm")
                || lowerQuery.contains("chart") || lowerQuery.contains("verteilung") || lowerQuery.contains("wie viele")
                || lowerQuery.contains("anzahl") || lowerQuery.contains("zähle") || lowerQuery.contains("umsatz")
                || lowerQuery.contains("trend") || lowerQuery.contains("torte") || lowerQuery.contains("balken");

        return hasListKeywords && !hasStatsKeywords;
    }

    /**
     * Detect customer filter from the query. Returns the matching customer name or
     * null if no filter detected.
     */
    private String detectCustomerFilter(String query, String currentUserId) {
        String lowerQuery = query.toLowerCase();

        // Keywords that indicate a customer filter
        String[] filterIndicators = { "für ", "von ", "bei ", "kunde ", "firma ", "unternehmen ", "für die firma ",
                "für den kunden ", "von der firma ", "vom kunden ", "nur ", "ausschließlich ", "speziell " };

        // Check if any indicator is present
        boolean hasIndicator = false;
        String foundIndicator = null;
        for (String indicator : filterIndicators) {
            int idx = lowerQuery.indexOf(indicator);
            if (idx >= 0) {
                hasIndicator = true;
                foundIndicator = indicator;
                break;
            }
        }

        if (!hasIndicator) {
            log.debug("detectCustomerFilter - No filter indicator found in: '{}'", query);
            return null;
        }

        log.debug("detectCustomerFilter - Found indicator '{}' in query: '{}'", foundIndicator, query);

        // Try to find a matching customer in the query
        String matchedCustomer = statisticsService.findMatchingCustomerForUser(currentUserId, query);
        log.debug("detectCustomerFilter - Matched customer: '{}'", matchedCustomer);
        return matchedCustomer;
    }

    private String buildCountChartData(String currentUserId, String customerFilter, JobStatus statusFilter) {
        long count = getFilteredJobCount(currentUserId, customerFilter, statusFilter);
        String label = buildCountMetricLabel(customerFilter, statusFilter);
        return buildChartJson(List.of(label), List.of(count), List.of("#3b82f6"), "Aufträge");
    }

    private String buildStatusChartData(String currentUserId, String customerFilter) {
        Map<JobStatus, Long> statusCounts = customerFilter != null
                ? statisticsService.getJobCountsByStatusForCustomerForUser(currentUserId, customerFilter)
                : statisticsService.getJobCountsByStatusForUser(currentUserId);

        List<String> labels = new ArrayList<>();
        List<Long> data = new ArrayList<>();
        // Moderne Farbpalette mit satteren Farben
        List<String> colors = List.of("#06b6d4", // CREATED - cyan
                "#f59e0b", // IN_PROGRESS - amber
                "#3b82f6", // PICKUP_SCHEDULED - blau
                "#8b5cf6", // PICKED_UP - violett
                "#f97316", // IN_TRANSIT - orange
                "#22c55e", // DELIVERED - grün
                "#6366f1", // COMPLETED - indigo
                "#ef4444" // CANCELLED - rot
        );

        for (JobStatus status : JobStatus.values()) {
            Long count = statusCounts.getOrDefault(status, 0L);
            if (count > 0) {
                labels.add(status.getDisplayName());
                data.add(count);
            }
        }

        String label = customerFilter != null ? "Aufträge (" + customerFilter + ")" : "Aufträge";
        return buildChartJson(labels, data, colors.subList(0, Math.min(labels.size(), colors.size())), label);
    }

    private String buildRevenueChartData(String currentUserId, String customerFilter, TimeScope timeScope,
            boolean revenueBreakdownRequested) {
        RevenueFacts facts = buildRevenueFacts(currentUserId, customerFilter, timeScope, revenueBreakdownRequested);

        if (revenueBreakdownRequested && !facts.topCustomers().isEmpty()) {
            List<String> labels = new ArrayList<>();
            List<Double> data = new ArrayList<>();
            for (var entry : facts.topCustomers()) {
                labels.add(entry.getKey() != null ? entry.getKey() : "Unbekannt");
                data.add(entry.getValue().doubleValue());
            }
            List<String> colors = List.of("#6366f1", "#8b5cf6", "#a855f7", "#c084fc", "#d8b4fe", "#e9d5ff",
                    "#f3e8ff", "#faf5ff", "#ede9fe", "#ddd6fe");
            return buildChartJsonDouble(labels, data, colors.subList(0, Math.min(labels.size(), colors.size())),
                    "Umsatz (EUR)");
        }

        return buildChartJsonDouble(List.of(buildRevenueMetricLabel(customerFilter, timeScope)),
                List.of(facts.totalRevenue().doubleValue()), List.of("#6366f1"), "Umsatz (EUR)");
    }

    private RevenueFacts buildRevenueFacts(String currentUserId, String customerFilter, TimeScope timeScope,
            boolean revenueBreakdownRequested) {
        if (timeScope == TimeScope.ALL_TIME) {
            if (customerFilter != null) {
                return new RevenueFacts(statisticsService.getTotalRevenueForCustomerForUser(currentUserId, customerFilter),
                        statisticsService.getTotalJobCountForCustomerForUser(currentUserId, customerFilter), List.of());
            }
            List<Map.Entry<String, BigDecimal>> topCustomers = revenueBreakdownRequested
                    ? statisticsService.getTopCustomersByRevenueForUser(currentUserId, 10)
                    : List.of();
            return new RevenueFacts(statisticsService.getTotalRevenueForUser(currentUserId),
                    statisticsService.getTotalJobCountForUser(currentUserId), topCustomers);
        }

        LocalDateTime start = getRangeStart(timeScope);
        LocalDateTime end = LocalDateTime.now();
        if (customerFilter != null) {
            return new RevenueFacts(
                    statisticsService.getTotalRevenueForCustomerForUserInRange(currentUserId, customerFilter, start, end),
                    statisticsService.getTotalJobCountForCustomerForUserInRange(currentUserId, customerFilter, start, end),
                    List.of());
        }

        List<Map.Entry<String, BigDecimal>> topCustomers = revenueBreakdownRequested
                ? statisticsService.getTopCustomersByRevenueForUserInRange(currentUserId, start, end, 10)
                : List.of();
        return new RevenueFacts(statisticsService.getTotalRevenueForUserInRange(currentUserId, start, end),
                statisticsService.getTotalJobCountForUserInRange(currentUserId, start, end), topCustomers);
    }

    private String buildTrendChartData(String currentUserId, String customerFilter) {
        int currentYear = Year.now().getValue();
        Map<Month, Long> monthlyData = customerFilter != null
                ? statisticsService.getMonthlyJobCountsForCustomerForUser(currentYear, currentUserId, customerFilter)
                : statisticsService.getMonthlyJobCountsForUser(currentYear, currentUserId);

        List<String> labels = List.of("Jan", "Feb", "Mär", "Apr", "Mai", "Jun", "Jul", "Aug", "Sep", "Okt", "Nov",
                "Dez");
        List<Long> data = new ArrayList<>();

        for (Month month : Month.values()) {
            data.add(monthlyData.getOrDefault(month, 0L));
        }

        String datasetLabel = customerFilter != null ? String.format("%s - %d", customerFilter, currentYear)
                : String.format("Aufträge %d", currentYear);

        return String.format("""
                {
                    "labels": %s,
                    "datasets": [{
                        "label": "%s",
                        "data": %s,
                        "borderColor": "#6366f1",
                        "backgroundColor": "rgba(99, 102, 241, 0.15)",
                        "pointBackgroundColor": "#6366f1",
                        "pointBorderColor": "#fff",
                        "pointBorderWidth": 2,
                        "pointRadius": 5,
                        "pointHoverRadius": 7,
                        "tension": 0.4,
                        "fill": true
                    }]
                }
                """, toJsonArray(labels), datasetLabel, data);
    }

    private String buildTaskChartData(String currentUserId) {
        Map<String, Long> taskStats = statisticsService.getTaskCompletionStatsForUser(currentUserId);

        List<String> labels = List.of("Erledigt", "Ausstehend");
        List<Long> data = List.of(taskStats.getOrDefault("completed", 0L), taskStats.getOrDefault("pending", 0L));
        List<String> colors = List.of("#22c55e", "#f59e0b");

        return buildChartJson(labels, data, colors, "Aufgaben");
    }

    private String buildOverviewChartData(String currentUserId, String customerFilter) {
        Map<JobStatus, Long> statusCounts = customerFilter != null
                ? statisticsService.getJobCountsByStatusForCustomerForUser(currentUserId, customerFilter)
                : statisticsService.getJobCountsByStatusForUser(currentUserId);

        long total = customerFilter != null
                ? statisticsService.getTotalJobCountForCustomerForUser(currentUserId, customerFilter)
                : statisticsService.getTotalJobCountForUser(currentUserId);
        long completed = statusCounts.getOrDefault(JobStatus.COMPLETED, 0L);
        long inProgress = statusCounts.getOrDefault(JobStatus.IN_PROGRESS, 0L);
        long open = total - completed - statusCounts.getOrDefault(JobStatus.CANCELLED, 0L);

        List<String> labels = List.of("Gesamt", "Abgeschlossen", "In Bearbeitung", "Offen");
        List<Long> data = List.of(total, completed, inProgress, open);
        List<String> colors = List.of("#3b82f6", "#22c55e", "#f59e0b", "#06b6d4");

        String label = customerFilter != null ? customerFilter : "Aufträge";
        return buildChartJson(labels, data, colors, label);
    }

    private String buildChartJson(List<String> labels, List<Long> data, List<String> colors, String label) {
        return String.format("""
                {
                    "labels": %s,
                    "datasets": [{
                        "label": "%s",
                        "data": %s,
                        "backgroundColor": %s,
                        "borderWidth": 1
                    }]
                }
                """, toJsonArray(labels), label, data, toJsonArray(colors));
    }

    private String buildChartJsonDouble(List<String> labels, List<Double> data, List<String> colors, String label) {
        return String.format("""
                {
                    "labels": %s,
                    "datasets": [{
                        "label": "%s",
                        "data": %s,
                        "backgroundColor": %s,
                        "borderWidth": 1
                    }]
                }
                """, toJsonArray(labels), label, data, toJsonArray(colors));
    }

    private String toJsonArray(List<String> list) {
        try {
            return objectMapper.writeValueAsString(list);
        } catch (JsonProcessingException e) {
            return "[]";
        }
    }

    private String buildStatisticsContext(String currentUserId, QueryAnalysis analysis) {
        StringBuilder context = new StringBuilder();
        String customerFilter = analysis.customerFilter;
        JobStatus statusFilter = analysis.statusFilter;

        // For LIST queries, show actual job data
        if ("list".equals(analysis.queryType)) {
            return buildListContext(currentUserId, customerFilter, statusFilter);
        }

        if ("count".equals(analysis.queryType)) {
            return buildCountContext(currentUserId, customerFilter, statusFilter);
        }

        if ("revenue".equals(analysis.queryType)) {
            return buildRevenueContext(currentUserId, analysis);
        }

        // For statistics queries
        if (customerFilter != null) {
            // Filtered statistics for a specific customer
            context.append(
                    String.format("**Statistiken des angemeldeten Benutzers für Kunde: %s**\n\n", customerFilter));

            var statusCounts = statisticsService.getJobCountsByStatusForCustomerForUser(currentUserId, customerFilter);
            context.append("**Aufträge nach Status:**\n");
            statusCounts.forEach((status, count) -> {
                if (count > 0) {
                    context.append(String.format("- %s: %d Aufträge\n", status.getDisplayName(), count));
                }
            });

            context.append(String.format("\n**Übersicht für %s:**\n", customerFilter));
            context.append(String.format("- Gesamtanzahl Aufträge: %d\n",
                    statisticsService.getTotalJobCountForCustomerForUser(currentUserId, customerFilter)));
            context.append(String.format("- Abschlussrate: %.1f%%\n",
                    statisticsService.getCompletionRateForCustomerForUser(currentUserId, customerFilter)));
            context.append(String.format("- Umsatz: %.2f EUR\n",
                    statisticsService.getTotalRevenueForCustomerForUser(currentUserId, customerFilter)));
        } else {
            // General statistics (current user only)
            var statusCounts = statisticsService.getJobCountsByStatusForUser(currentUserId);
            context.append("**Aktuelle Auftragsstatistiken des angemeldeten Benutzers:**\n");
            statusCounts.forEach((status, count) -> context
                    .append(String.format("- %s: %d Aufträge\n", status.getDisplayName(), count)));

            context.append(String.format("\n**Gesamtübersicht:**\n"));
            context.append(String.format("- Gesamtanzahl Aufträge: %d\n",
                    statisticsService.getTotalJobCountForUser(currentUserId)));
            context.append(String.format("- Abschlussrate: %.1f%%\n",
                    statisticsService.getCompletionRateForUser(currentUserId)));
            context.append(String.format("- Gesamtumsatz: %.2f EUR\n",
                    statisticsService.getTotalRevenueForUser(currentUserId)));

            // Task statistics
            var taskStats = statisticsService.getTaskCompletionStatsForUser(currentUserId);
            context.append(String.format("\n**Aufgaben:**\n"));
            context.append(String.format("- Gesamt: %d\n", taskStats.get("total")));
            context.append(String.format("- Erledigt: %d\n", taskStats.get("completed")));
            context.append(String.format("- Ausstehend: %d\n", taskStats.get("pending")));

            // Top customers
            var topCustomers = statisticsService.getTopCustomersByRevenueForUser(currentUserId, 5);
            if (!topCustomers.isEmpty()) {
                context.append("\n**Top 5 Kunden nach Umsatz:**\n");
                for (var entry : topCustomers) {
                    context.append(String.format("- %s: %.2f EUR\n",
                            entry.getKey() != null ? entry.getKey() : "Unbekannt", entry.getValue()));
                }
            }
        }

        return context.toString();
    }

    private String buildRevenueContext(String currentUserId, QueryAnalysis analysis) {
        RevenueFacts facts = buildRevenueFacts(currentUserId, analysis.customerFilter, analysis.timeScope,
                analysis.revenueBreakdownRequested);

        StringBuilder context = new StringBuilder("**Verbindliche Umsatzdaten des angemeldeten Benutzers:**\n");
        context.append(String.format("- Zeitraum: %s\n", buildTimeScopeLabel(analysis.timeScope)));
        if (analysis.customerFilter != null) {
            context.append(String.format("- Kunde: %s\n", analysis.customerFilter));
        }
        context.append(String.format("- Maßgeblicher Gesamtumsatz: %s\n", formatCurrency(facts.totalRevenue())));
        context.append(String.format("- Zugehörige Aufträge: %d\n", facts.jobCount()));

        if (analysis.revenueBreakdownRequested && !facts.topCustomers().isEmpty()) {
            context.append("\n**Umsatz nach Kunden:**\n");
            for (var entry : facts.topCustomers()) {
                context.append(String.format("- %s: %s\n", entry.getKey() != null ? entry.getKey() : "Unbekannt",
                        formatCurrency(entry.getValue())));
            }
        }

        context.append("\n**Hinweis:**\n");
        context.append("- Die Zeile \"Maßgeblicher Gesamtumsatz\" ist die verbindliche Antwortgröße\n");
        return context.toString();
    }

    private String buildListContext(String currentUserId, String customerFilter, JobStatus statusFilter) {
        StringBuilder context = new StringBuilder();

        List<Job> jobs;
        if (customerFilter != null && statusFilter != null) {
            jobs = statisticsService.getJobsByCustomerAndStatusForUser(currentUserId, customerFilter, statusFilter);
            context.append(String.format("**Jobs des angemeldeten Benutzers für %s mit Status %s:**\n\n",
                    customerFilter, statusFilter.getDisplayName()));
        } else if (customerFilter != null) {
            jobs = statisticsService.getJobsByCustomerForUser(currentUserId, customerFilter);
            context.append(String.format("**Jobs des angemeldeten Benutzers für %s:**\n\n", customerFilter));
        } else if (statusFilter != null) {
            jobs = statisticsService.getJobsByStatusForUser(currentUserId, statusFilter);
            context.append(String.format("**Jobs des angemeldeten Benutzers mit Status %s:**\n\n",
                    statusFilter.getDisplayName()));
        } else {
            jobs = statisticsService.getLatestJobsForUser(currentUserId, 20);
            context.append("**Aktuelle Jobs des angemeldeten Benutzers:**\n\n");
        }

        context.append(String.format("Gefunden: %d Jobs\n\n", jobs.size()));

        // Show job summaries (max 10)
        int shown = 0;
        for (Job job : jobs) {
            if (shown >= 10) {
                context.append(String.format("... und %d weitere Jobs\n", jobs.size() - 10));
                break;
            }
            context.append(
                    String.format("- %s: %s (%s)\n", job.getJobNumber() != null ? job.getJobNumber() : "Ohne Nr.",
                            job.getCustomerSelection() != null ? job.getCustomerSelection() : "Unbekannt",
                            job.getStatus().getDisplayName()));
            shown++;
        }

        return context.toString();
    }

    private String buildCountContext(String currentUserId, String customerFilter, JobStatus statusFilter) {
        long count = getFilteredJobCount(currentUserId, customerFilter, statusFilter);
        String metricLabel = buildCountMetricLabel(customerFilter, statusFilter);

        return String.format("""
                **Verbindliche Zählung des angemeldeten Benutzers:**
                - %s: %d

                **Hinweis:**
                - Diese Zahl ist die maßgebliche Kennzahl für die Antwort
                """, metricLabel, count);
    }

    private String buildPrompt(String userQuery, String statisticsContext, QueryAnalysis analysis) {
        // User prompt contains only the context and question (system prompt is passed
        // separately)
        return String.format("""
                Antworte ausschließlich mit einem JSON-Objekt im geforderten Format.
                Gib keine Denkblöcke, keine Analyse und keine Tags wie <think> aus.
                Paraphrasiere die Frage nicht.
                Verwende keine Tabellen im Antworttext.
                Gewünschtes Diagramm angefordert: %s
                Gewünschte Tabelle angefordert: %s
                Setze "showChart" nur dann auf true, wenn das angeforderte Diagramm wirklich sinnvoll ist.
                Setze "showTable" nur dann auf true, wenn die angeforderte Tabelle wirklich sinnvoll ist.
                Ein Diagramm ist nicht sinnvoll, wenn es zu wenige aussagekräftige Datenpunkte gibt, fast alle Werte 0 sind
                oder die vorhandenen Daten nicht sauber zur angefragten Darstellung passen.
                Jede genannte Zahl muss exakt dem bereitgestellten Kontext entsprechen.
                Wenn ein Diagramm oder eine Tabelle nicht sinnvoll ist, setze nur das Flag auf false und schreibe keinen Hinweis
                wie "Die Anzeige von Diagrammen ist nicht möglich." in die Antwort.

                Hinweis: Der folgende Kontext enthält ausschließlich Daten des aktuell angemeldeten Benutzers.

                %s

                **Benutzerfrage:** %s
                """, analysis.chartType != null, analysis.tableRequested, statisticsContext, userQuery);
    }

    private LlmVisualizationDecision parseVisualizationDecision(String llmResponse, QueryAnalysis analysis,
            String currentUserId) {
        try {
            String json = extractJsonObject(llmResponse);
            var root = objectMapper.readTree(json);
            String answer = root.path("answer").asText("").trim();
            if (answer.isBlank()) {
                answer = buildFallbackResponse(currentUserId, analysis);
            }
            boolean showChart = parseDecisionFlag(root.path("showChart")) && analysis.chartType != null
                    && analysis.chartData != null;
            boolean showTable = parseDecisionFlag(root.path("showTable")) && analysis.tableRequested;
            return new LlmVisualizationDecision(answer, showChart, showTable);
        } catch (Exception e) {
            log.warn("Could not parse structured visualization decision: {}", e.getMessage());
            return new LlmVisualizationDecision(buildFallbackResponse(currentUserId, analysis), false, false);
        }
    }

    private boolean parseDecisionFlag(com.fasterxml.jackson.databind.JsonNode node) {
        if (node == null || node.isMissingNode() || node.isNull()) {
            return false;
        }
        if (node.isBoolean()) {
            return node.asBoolean();
        }
        if (node.isTextual()) {
            String value = node.asText("").trim().toLowerCase();
            return "true".equals(value) || "yes".equals(value) || "ja".equals(value) || "1".equals(value);
        }
        if (node.isInt() || node.isLong()) {
            return node.asInt() != 0;
        }
        return false;
    }

    private String extractJsonObject(String raw) {
        int start = raw.indexOf('{');
        int end = raw.lastIndexOf('}');
        if (start >= 0 && end > start) {
            return raw.substring(start, end + 1);
        }
        return raw;
    }

    private String buildCountTableHtml(String currentUserId, String customerFilter, JobStatus statusFilter) {
        long count = getFilteredJobCount(currentUserId, customerFilter, statusFilter);
        String metricLabel = buildCountMetricLabel(customerFilter, statusFilter);
        return buildHtmlTable(List.of("Kennzahl", "Wert"), List.of(List.of(metricLabel, String.valueOf(count))));
    }

    private String buildJobsTableHtml(String currentUserId, String customerFilter, JobStatus statusFilter) {
        List<Job> jobs;
        if (customerFilter != null && statusFilter != null) {
            jobs = statisticsService.getJobsByCustomerAndStatusForUser(currentUserId, customerFilter, statusFilter);
        } else if (customerFilter != null) {
            jobs = statisticsService.getJobsByCustomerForUser(currentUserId, customerFilter);
        } else if (statusFilter != null) {
            jobs = statisticsService.getJobsByStatusForUser(currentUserId, statusFilter);
        } else {
            jobs = statisticsService.getLatestJobsForUser(currentUserId, 20);
        }

        List<List<String>> rows = jobs.stream().limit(20)
                .map(job -> List.of(job.getJobNumber() != null ? job.getJobNumber() : "Ohne Nr.",
                        job.getCustomerSelection() != null ? job.getCustomerSelection() : "Unbekannt",
                        job.getStatus() != null ? job.getStatus().getDisplayName() : "Unbekannt"))
                .toList();
        return buildHtmlTable(List.of("Jobnummer", "Kunde", "Status"), rows);
    }

    private String buildStatusTableHtml(String currentUserId, String customerFilter) {
        Map<JobStatus, Long> counts = customerFilter != null
                ? statisticsService.getJobCountsByStatusForCustomerForUser(currentUserId, customerFilter)
                : statisticsService.getJobCountsByStatusForUser(currentUserId);

        List<List<String>> rows = new ArrayList<>();
        for (JobStatus status : JobStatus.values()) {
            rows.add(List.of(status.getDisplayName(), String.valueOf(counts.getOrDefault(status, 0L))));
        }
        return buildHtmlTable(List.of("Status", "Anzahl"), rows);
    }

    private String buildRevenueTableHtml(String currentUserId, QueryAnalysis analysis) {
        RevenueFacts facts = buildRevenueFacts(currentUserId, analysis.customerFilter, analysis.timeScope,
                analysis.revenueBreakdownRequested);

        if (analysis.revenueBreakdownRequested && !facts.topCustomers().isEmpty()) {
            List<List<String>> rows = facts.topCustomers().stream()
                    .map(entry -> List.of(entry.getKey() != null ? entry.getKey() : "Unbekannt",
                            formatCurrency(entry.getValue())))
                    .toList();
            return buildHtmlTable(List.of("Kunde", "Umsatz"), rows);
        }

        List<List<String>> rows = List.of(
                List.of("Zeitraum", buildTimeScopeLabel(analysis.timeScope)),
                List.of("Kennzahl", buildRevenueMetricLabel(analysis.customerFilter, analysis.timeScope)),
                List.of("Aufträge", String.valueOf(facts.jobCount())),
                List.of("Umsatz", formatCurrency(facts.totalRevenue())));
        return buildHtmlTable(List.of("Merkmal", "Wert"), rows);
    }

    private String buildTrendTableHtml(String currentUserId, String customerFilter) {
        int year = Year.now().getValue();
        Map<Month, Long> monthlyData = customerFilter != null
                ? statisticsService.getMonthlyJobCountsForCustomerForUser(year, currentUserId, customerFilter)
                : statisticsService.getMonthlyJobCountsForUser(year, currentUserId);

        List<List<String>> rows = new ArrayList<>();
        for (Month month : Month.values()) {
            rows.add(List.of(month.name(), String.valueOf(monthlyData.getOrDefault(month, 0L))));
        }
        return buildHtmlTable(List.of("Monat", "Aufträge"), rows);
    }

    private String buildTasksTableHtml(String currentUserId) {
        Map<String, Long> taskStats = statisticsService.getTaskCompletionStatsForUser(currentUserId);
        List<List<String>> rows = List.of(List.of("Gesamt", String.valueOf(taskStats.getOrDefault("total", 0L))),
                List.of("Erledigt", String.valueOf(taskStats.getOrDefault("completed", 0L))),
                List.of("Ausstehend", String.valueOf(taskStats.getOrDefault("pending", 0L))));
        return buildHtmlTable(List.of("Kennzahl", "Wert"), rows);
    }

    private String buildOverviewTableHtml(String currentUserId, String customerFilter) {
        if (customerFilter != null) {
            List<List<String>> rows = List.of(
                    List.of("Aufträge", String.valueOf(
                            statisticsService.getTotalJobCountForCustomerForUser(currentUserId, customerFilter))),
                    List.of("Abschlussrate", String.format("%.1f%%",
                            statisticsService.getCompletionRateForCustomerForUser(currentUserId, customerFilter))),
                    List.of("Umsatz", String.format("%.2f EUR",
                            statisticsService.getTotalRevenueForCustomerForUser(currentUserId, customerFilter))));
            return buildHtmlTable(List.of("Kennzahl", "Wert"), rows);
        }

        List<List<String>> rows = List.of(
                List.of("Aufträge", String.valueOf(statisticsService.getTotalJobCountForUser(currentUserId))),
                List.of("Abschlussrate",
                        String.format("%.1f%%", statisticsService.getCompletionRateForUser(currentUserId))),
                List.of("Gesamtumsatz", String.format("%.2f EUR", statisticsService.getTotalRevenueForUser(currentUserId))));
        return buildHtmlTable(List.of("Kennzahl", "Wert"), rows);
    }

    private String buildHtmlTable(List<String> headers, List<List<String>> rows) {
        StringBuilder html = new StringBuilder();
        html.append(
                "<table style='width:100%;border-collapse:collapse;margin-top:var(--lumo-space-m);font-size:var(--lumo-font-size-s);'>");
        html.append("<thead><tr>");
        for (String header : headers) {
            html.append("<th style='text-align:left;padding:8px;border-bottom:1px solid var(--lumo-contrast-20pct);'>")
                    .append(escapeHtml(header)).append("</th>");
        }
        html.append("</tr></thead><tbody>");

        for (List<String> row : rows) {
            html.append("<tr>");
            for (String cell : row) {
                html.append(
                        "<td style='padding:8px;border-bottom:1px solid var(--lumo-contrast-10pct);vertical-align:top;'>")
                        .append(escapeHtml(cell)).append("</td>");
            }
            html.append("</tr>");
        }

        html.append("</tbody></table>");
        return html.toString();
    }

    private String escapeHtml(String value) {
        if (value == null) {
            return "";
        }
        return value.replace("&", "&amp;").replace("<", "&lt;").replace(">", "&gt;").replace("\"", "&quot;")
                .replace("'", "&#39;");
    }

    private LocalDateTime getRangeStart(TimeScope timeScope) {
        return switch (timeScope) {
        case CURRENT_MONTH -> YearMonth.now().atDay(1).atStartOfDay();
        case CURRENT_YEAR -> Year.now().atDay(1).atStartOfDay();
        case ALL_TIME -> LocalDateTime.MIN;
        };
    }

    private String buildTimeScopeLabel(TimeScope timeScope) {
        return switch (timeScope) {
        case CURRENT_MONTH -> {
            YearMonth yearMonth = YearMonth.now();
            String monthName = yearMonth.getMonth().getDisplayName(TextStyle.FULL, Locale.GERMAN);
            yield monthName.substring(0, 1).toUpperCase(Locale.GERMAN) + monthName.substring(1) + " "
                    + yearMonth.getYear();
        }
        case CURRENT_YEAR -> String.valueOf(Year.now().getValue());
        case ALL_TIME -> "Gesamt";
        };
    }

    private String buildTimeScopePhrase(TimeScope timeScope) {
        return switch (timeScope) {
        case CURRENT_MONTH -> "in diesem Monat";
        case CURRENT_YEAR -> "in diesem Jahr";
        case ALL_TIME -> "insgesamt";
        };
    }

    private String buildRevenueMetricLabel(String customerFilter, TimeScope timeScope) {
        String scopeLabel = switch (timeScope) {
        case CURRENT_MONTH -> "Umsatz im Monat";
        case CURRENT_YEAR -> "Umsatz im Jahr";
        case ALL_TIME -> "Gesamtumsatz";
        };

        if (customerFilter != null) {
            return scopeLabel + " für " + customerFilter;
        }
        return scopeLabel;
    }

    private String formatCurrency(BigDecimal amount) {
        return String.format(Locale.GERMAN, "%.2f EUR", amount);
    }

    private String selectAnswerText(String llmAnswer, String currentUserId, QueryAnalysis analysis) {
        if (shouldUseDeterministicAnswer(analysis)) {
            return buildFallbackResponse(currentUserId, analysis);
        }
        if (llmAnswer == null || llmAnswer.isBlank()) {
            return buildFallbackResponse(currentUserId, analysis);
        }
        return llmAnswer.trim();
    }

    private boolean shouldUseDeterministicAnswer(QueryAnalysis analysis) {
        return switch (analysis.queryType) {
        case "count", "status", "overview", "revenue", "tasks" -> true;
        default -> false;
        };
    }

    private long getFilteredJobCount(String currentUserId, String customerFilter, JobStatus statusFilter) {
        if (customerFilter != null && statusFilter != null) {
            return statisticsService.getJobsByCustomerAndStatusForUser(currentUserId, customerFilter, statusFilter)
                    .size();
        }
        if (customerFilter != null) {
            return statisticsService.getTotalJobCountForCustomerForUser(currentUserId, customerFilter);
        }
        if (statusFilter != null) {
            return statisticsService.getJobsByStatusForUser(currentUserId, statusFilter).size();
        }
        return statisticsService.getTotalJobCountForUser(currentUserId);
    }

    private String buildCountMetricLabel(String customerFilter, JobStatus statusFilter) {
        if (customerFilter != null && statusFilter != null) {
            return String.format("%s (%s)", customerFilter, statusFilter.getDisplayName());
        }
        if (customerFilter != null) {
            return String.format("Aufträge für %s", customerFilter);
        }
        if (statusFilter != null) {
            return String.format("Aufträge mit Status %s", statusFilter.getDisplayName());
        }
        return "Aufträge gesamt";
    }

    private String formatOrderCount(long count) {
        return count == 1 ? "1 Auftrag" : count + " Aufträge";
    }

    private String buildRevenueAnswer(String currentUserId, QueryAnalysis analysis) {
        RevenueFacts facts = buildRevenueFacts(currentUserId, analysis.customerFilter, analysis.timeScope,
                analysis.revenueBreakdownRequested);

        if (analysis.amountOnlyRequested) {
            return formatCurrency(facts.totalRevenue()) + ".";
        }

        if (analysis.customerFilter != null) {
            return String.format("Ihr Umsatz %s für %s beträgt %s.", buildTimeScopePhrase(analysis.timeScope),
                    analysis.customerFilter, formatCurrency(facts.totalRevenue()));
        }

        if (analysis.revenueBreakdownRequested && !facts.topCustomers().isEmpty()) {
            StringBuilder answer = new StringBuilder();
            int shown = 0;
            for (var entry : facts.topCustomers()) {
                if (shown >= 3) {
                    break;
                }
                if (!answer.isEmpty()) {
                    answer.append(" ");
                }
                answer.append(String.format("%d. %s: %s.", shown + 1,
                        entry.getKey() != null ? entry.getKey() : "Unbekannt", formatCurrency(entry.getValue())));
                shown++;
            }
            if (!answer.isEmpty()) {
                answer.append(" ");
            }
            answer.append(String.format("Gesamtumsatz %s: %s.", buildTimeScopePhrase(analysis.timeScope),
                    formatCurrency(facts.totalRevenue())));
            return answer.toString();
        }

        return String.format("Ihr Umsatz %s beträgt %s.", buildTimeScopePhrase(analysis.timeScope),
                formatCurrency(facts.totalRevenue()));
    }

    private String buildFallbackResponse(String currentUserId, QueryAnalysis analysis) {
        String customer = analysis.customerFilter;
        JobStatus statusFilter = analysis.statusFilter;

        return switch (analysis.queryType) {
        case "count" -> {
            long count = getFilteredJobCount(currentUserId, customer, statusFilter);
            if (customer != null && statusFilter != null) {
                yield String.format("Sie haben aktuell %s für %s mit Status %s.", formatOrderCount(count), customer,
                        statusFilter.getDisplayName());
            }
            if (customer != null) {
                yield String.format("Sie haben aktuell %s für %s.", formatOrderCount(count), customer);
            }
            if (statusFilter != null) {
                yield String.format("Sie haben aktuell %s mit Status %s.", formatOrderCount(count),
                        statusFilter.getDisplayName());
            }
            yield String.format("Sie haben aktuell insgesamt %s.", formatOrderCount(count));
        }
        case "list" -> {
            List<Job> jobs;
            if (customer != null && statusFilter != null) {
                jobs = statisticsService.getJobsByCustomerAndStatusForUser(currentUserId, customer, statusFilter);
                yield String.format("%d Jobs für %s mit Status %s.", jobs.size(), customer,
                        statusFilter.getDisplayName());
            } else if (customer != null) {
                jobs = statisticsService.getJobsByCustomerForUser(currentUserId, customer);
                yield String.format("%d Jobs für %s.", jobs.size(), customer);
            } else if (statusFilter != null) {
                jobs = statisticsService.getJobsByStatusForUser(currentUserId, statusFilter);
                yield String.format("%d Jobs mit Status %s.", jobs.size(), statusFilter.getDisplayName());
            } else {
                jobs = statisticsService.getLatestJobsForUser(currentUserId, 20);
                yield String.format("%d aktuelle Jobs.", jobs.size());
            }
        }
        case "status" -> {
            var counts = customer != null
                    ? statisticsService.getJobCountsByStatusForCustomerForUser(currentUserId, customer)
                    : statisticsService.getJobCountsByStatusForUser(currentUserId);
            StringBuilder sb = new StringBuilder();
            counts.forEach((status, count) -> {
                if (count > 0) {
                    if (!sb.isEmpty()) {
                        sb.append(" ");
                    }
                    sb.append(String.format("%s: %d.", status.getDisplayName(), count));
                }
            });
            long total = customer != null
                    ? statisticsService.getTotalJobCountForCustomerForUser(currentUserId, customer)
                    : statisticsService.getTotalJobCountForUser(currentUserId);
            if (!sb.isEmpty()) {
                sb.append(" ");
            }
            sb.append(String.format("Gesamt: %d Aufträge.", total));
            yield sb.toString();
        }
        case "revenue" -> {
            yield buildRevenueAnswer(currentUserId, analysis);
        }
        case "trend" -> {
            int year = Year.now().getValue();
            var monthly = customer != null
                    ? statisticsService.getMonthlyJobCountsForCustomerForUser(year, currentUserId, customer)
                    : statisticsService.getMonthlyJobCountsForUser(year, currentUserId);
            long total = monthly.values().stream().mapToLong(Long::longValue).sum();
            if (customer != null) {
                yield String.format("%d wurden für %s insgesamt %d Aufträge erstellt.", year, customer, total);
            }
            yield String.format("%d wurden insgesamt %d Aufträge erstellt.", year, total);
        }
        case "tasks" -> {
            var taskStats = statisticsService.getTaskCompletionStatsForUser(currentUserId);
            long total = taskStats.getOrDefault("total", 0L);
            long completed = taskStats.getOrDefault("completed", 0L);
            double rate = total > 0 ? (double) completed / total * 100 : 0;
            yield String.format("%d von %d Aufgaben erledigt (%.1f%%), %d ausstehend.", completed, total, rate,
                    taskStats.getOrDefault("pending", 0L));
        }
        default -> {
            if (customer != null) {
                yield String.format("%s: %d Aufträge, Abschlussrate %.1f%%, Umsatz %.2f EUR.", customer,
                        statisticsService.getTotalJobCountForCustomerForUser(currentUserId, customer),
                        statisticsService.getCompletionRateForCustomerForUser(currentUserId, customer),
                        statisticsService.getTotalRevenueForCustomerForUser(currentUserId, customer));
            } else {
                yield String.format("%d Aufträge, Abschlussrate %.1f%%, Gesamtumsatz %.2f EUR.",
                        statisticsService.getTotalJobCountForUser(currentUserId),
                        statisticsService.getCompletionRateForUser(currentUserId),
                        statisticsService.getTotalRevenueForUser(currentUserId));
            }
        }
        };
    }
}
