package de.frigosped.dc.service;

import de.frigosped.dc.client.OrdsClient;
import de.frigosped.dc.model.AiCostRequest;
import dev.langchain4j.model.output.TokenUsage;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import org.eclipse.microprofile.config.inject.ConfigProperty;
import org.eclipse.microprofile.rest.client.inject.RestClient;
import org.jboss.logging.Logger;

import java.util.concurrent.CompletableFuture;

/**
 * Erfasst Token-Verbrauch und Kosten für jeden KI-Aufruf und persistiert
 * sie asynchron via ORDS in dc_ai_cost_log.
 *
 * Aufruf-Muster (qualifier = "main" | "catalog" | "ocr"):
 *   costTracker.track("main", "TRANSLATE", projectId, null, response.tokenUsage());
 */
@ApplicationScoped
public class AiCostTrackerService {

    private static final Logger LOG = Logger.getLogger(AiCostTrackerService.class);

    @ConfigProperty(name = "dc.ai.provider", defaultValue = "ollama")
    String provider;

    // Modellnamen je Provider
    @ConfigProperty(name = "dc.ai.ollama.main.model",    defaultValue = "") String ollamaMainModel;
    @ConfigProperty(name = "dc.ai.ollama.catalog.model", defaultValue = "") String ollamaCatalogModel;
    @ConfigProperty(name = "dc.ai.ollama.ocr.model",     defaultValue = "") String ollamaOcrModel;

    @ConfigProperty(name = "dc.ai.mistral.main.model",    defaultValue = "mistral-small-latest") String mistralMainModel;
    @ConfigProperty(name = "dc.ai.mistral.catalog.model", defaultValue = "mistral-small-latest") String mistralCatalogModel;
    @ConfigProperty(name = "dc.ai.mistral.ocr.model",     defaultValue = "pixtral-12b-2409")     String mistralOcrModel;

    // Preise in EUR pro 1 Mio. Tokens (Mistral; Ollama = 0)
    @ConfigProperty(name = "dc.ai.mistral.main.price-input-per-1m-eur",    defaultValue = "0.092") double mistralMainInput;
    @ConfigProperty(name = "dc.ai.mistral.main.price-output-per-1m-eur",   defaultValue = "0.276") double mistralMainOutput;
    @ConfigProperty(name = "dc.ai.mistral.catalog.price-input-per-1m-eur", defaultValue = "0.092") double mistralCatalogInput;
    @ConfigProperty(name = "dc.ai.mistral.catalog.price-output-per-1m-eur",defaultValue = "0.276") double mistralCatalogOutput;
    @ConfigProperty(name = "dc.ai.mistral.ocr.price-input-per-1m-eur",     defaultValue = "0.138") double mistralOcrInput;
    @ConfigProperty(name = "dc.ai.mistral.ocr.price-output-per-1m-eur",    defaultValue = "0.138") double mistralOcrOutput;

    @Inject
    @RestClient
    OrdsClient ordsClient;

    /**
     * @param qualifier   "main" | "catalog" | "ocr"
     * @param operation   "TRANSLATE" | "EVALUATE" | "OCR" | "CATALOG"
     * @param projectId   Projekt-ID (null bei Katalog-Operationen)
     * @param catalogId   Katalog-ID (null bei Prüf-Operationen)
     * @param tokenUsage  aus response.tokenUsage() – null wird als 0 behandelt
     */
    public void track(String qualifier, String operation,
                      Long projectId, Long catalogId, TokenUsage tokenUsage) {

        int promptTokens     = safeCount(tokenUsage != null ? tokenUsage.inputTokenCount()  : null);
        int completionTokens = safeCount(tokenUsage != null ? tokenUsage.outputTokenCount() : null);
        int totalTokens      = safeCount(tokenUsage != null ? tokenUsage.totalTokenCount()  : null);
        double costEur       = calcCost(qualifier, promptTokens, completionTokens);

        AiCostRequest req = new AiCostRequest();
        req.setProvider(provider);
        req.setModelName(resolveModel(qualifier));
        req.setOperation(operation);
        req.setProjectId(projectId);
        req.setCatalogId(catalogId);
        req.setPromptTokens(promptTokens);
        req.setCompletionTokens(completionTokens);
        req.setTotalTokens(totalTokens);
        req.setCostEur(costEur);

        CompletableFuture.runAsync(() -> {
            try {
                ordsClient.logAiCost(req);
                LOG.debugf("Kosten erfasst: %s/%s %.6f EUR (%d tokens)",
                        qualifier, operation, costEur, totalTokens);
            } catch (Exception e) {
                LOG.warnf("Kostenverfolgung fehlgeschlagen (%s/%s): %s",
                        qualifier, operation, e.getMessage());
            }
        });
    }

    private String resolveModel(String qualifier) {
        boolean isMistral = "mistral".equalsIgnoreCase(provider);
        return switch (qualifier) {
            case "catalog" -> isMistral ? mistralCatalogModel : ollamaCatalogModel;
            case "ocr"     -> isMistral ? mistralOcrModel     : ollamaOcrModel;
            default        -> isMistral ? mistralMainModel     : ollamaMainModel;
        };
    }

    private double calcCost(String qualifier, int prompt, int completion) {
        if (!"mistral".equalsIgnoreCase(provider)) return 0.0;
        double inPrice, outPrice;
        switch (qualifier) {
            case "ocr"     -> { inPrice = mistralOcrInput;     outPrice = mistralOcrOutput; }
            case "catalog" -> { inPrice = mistralCatalogInput; outPrice = mistralCatalogOutput; }
            default        -> { inPrice = mistralMainInput;    outPrice = mistralMainOutput; }
        }
        return (prompt     / 1_000_000.0) * inPrice
             + (completion / 1_000_000.0) * outPrice;
    }

    private int safeCount(Integer value) {
        return value != null ? value : 0;
    }
}
