using Azure.Identity;
using Azure.Monitor.Query;
using Azure.Monitor.Query.Models;
using System.Text.Json;
using DF.AppUsage.Api.Data;
using DF.AppUsage.Api.Models;
using Microsoft.EntityFrameworkCore;

namespace DF.AppUsage.Api.Services;

public class AzureImportService(
    IConfiguration configuration, 
    IServiceScopeFactory scopeFactory, 
    ILogger<AzureImportService> logger) : BackgroundService
{
    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        while (!stoppingToken.IsCancellationRequested)
        {
            try
            {
                logger.LogInformation("Starte Azure-Import: {Time}", DateTimeOffset.Now);
                await ImportDataAsync();
            }
            catch (Exception ex)
            {
                // Hier fangen wir alles ab, damit der BackgroundService NICHT abstürzt
                logger.LogError(ex, "Ein Fehler ist beim Azure-Import aufgetreten. Versuche es in 15 Minuten erneut.");
            }

            // Alle 15 Minuten prüfen (Zeitspanne kannst du anpassen)
            await Task.Delay(TimeSpan.FromMinutes(15), stoppingToken);
        }
    }

    private async Task ImportDataAsync()
    {
        // Wir brauchen einen Scope, um auf die Datenbank zuzugreifen (da BackgroundService ein Singleton ist)
        using var scope = scopeFactory.CreateScope();
        var dbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();

        var tenantId = configuration["Azure:TenantId"];
        var clientId = configuration["Azure:ClientId"];
        var clientSecret = configuration["Azure:ClientSecret"];
        var workspaceId = configuration["Azure:WorkspaceId"];

        var credential = new ClientSecretCredential(tenantId, clientId, clientSecret);
        var client = new LogsQueryClient(credential);

        // --- HIER HOLEN WIR DIE DATEN ---
        // für BerichtGPT Nutzung
        await FetchAndStore(client, workspaceId, dbContext, "Feature_Use_AiBericht", "Feature_Use_AiBericht");
        
        // für LieferscheinGPT Nutzung
        await FetchAndStore(client, workspaceId, dbContext, "Feature_Use_AiLieferschein", "Feature_Use_AiLieferschein");

        // für Version-Ping
        await FetchAndStore(client, workspaceId, dbContext, "AppLaunched_NewVersion", "AppLaunched_NewVersion");
    }

    private async Task FetchAndStore(LogsQueryClient client, string workspaceId, AppDbContext dbContext, string azureEventName, string internalTypeName)
    {
        // 1. Letzten Eintrag holen
        var lastEntry = await dbContext.InsightEntries
            .Where(e => e.InsightType == internalTypeName)
            .OrderByDescending(e => e.Timestamp)
            .Select(e => new { e.Timestamp }) // Wir brauchen nur den Zeitstempel
            .FirstOrDefaultAsync();

        // Wenn vorhanden, nehmen wir den exakten Zeitstempel. 
        // Wir nutzen >= in der Query und verlassen uns darauf, dass Azure 
        // uns keine Dubletten schickt, wenn wir die Startzeit präzise übergeben.
        // Falls wir ganz sicher gehen wollen, addieren wir einen "Tick" (kleinste Einheit).
        DateTime startZeitUtc = lastEntry?.Timestamp ?? new DateTime(2020, 1, 1, 0, 0, 0, DateTimeKind.Utc);

        // WICHTIG: Wir nutzen in KQL das ">" Zeichen (echt größer als).
        // Damit sagen wir Azure: "Gib mir alles, was zeitlich NACH diesem Eintrag kam".
        // Die Formatierung "yyyy-MM-ddTHH:mm:ss.fffffffZ" stellt sicher, 
        // dass wir die volle Präzision (7 Nachkommastellen) an Azure senden.
        //string kqlStart = startZeitUtc.ToString("o");
        string kqlStart = startZeitUtc.ToString("yyyy-MM-ddTHH:mm:ss.fffffffZ");

        string kqlQuery = $@"
            AppEvents 
            | where Name == '{azureEventName}' 
            | where TimeGenerated > datetime({kqlStart})
            | project 
                Timestamp = TimeGenerated, 
                ExternalId = _ItemId,
                FullData = bag_pack('Properties', Properties, 'Measurements', Measurements)
            | sort by Timestamp asc"; // Wir sortieren aufsteigend für sauberes Logging

        // Der TimeRange muss mindestens am startZeitUtc beginnen. 
        // Zur Sicherheit nehmen wir einen kleinen Puffer nach hinten (AddSeconds(-1)), 
        // damit Azure das Fenster für die 'where' Clause weit genug öffnet.
        var timeRange = new QueryTimeRange(startZeitUtc.AddMinutes(-5), DateTimeOffset.UtcNow);

        var response = await client.QueryWorkspaceAsync(workspaceId, kqlQuery, timeRange);
        var rows = response.Value.Table.Rows;

        if (rows.Count == 0) return;

        foreach (var row in rows)
        {
            var timestamp = row.GetDateTimeOffset("Timestamp")!.Value.UtcDateTime;
            var extId = row.GetString("ExternalId");
            var jsonString = row.GetString("FullData");

            // Zusätzlicher Safety-Check im Code:
            // Falls Azure uns trotz ">" den exakt gleichen Zeitstempel nochmal schickt
            // if (lastEntry != null && timestamp <= lastEntry.Timestamp)
            // {
            //     continue; // Überspringen, da wir diesen Zeitpunkt schon haben
            // }
            // Sicherheit: Falls Azure uns (wegen Zeitfenster-Puffern) 
            // doch mal ein Event schickt, das wir schon haben:
            if (await dbContext.InsightEntries.AnyAsync(e => e.ExternalId == extId))
            {
                continue; 
            }

            var entry = new AppInsightEntry
            {
                ExternalId = extId, // <--- Hier speichern wir die ID
                Timestamp = timestamp,
                InsightType = internalTypeName,
                Data = JsonDocument.Parse(jsonString),
                RetrievedAt = DateTime.UtcNow
            };

            dbContext.InsightEntries.Add(entry);
        }

        await dbContext.SaveChangesAsync();
        logger.LogInformation("{Count} neue Einträge für {Type} ab {Start} gespeichert.", rows.Count, internalTypeName, kqlStart);
    }
}