using Dapper;
using FastEndpoints;
using Npgsql;
using Kbx.Shared.ExternalData.Generated;
using Kbx.Shared.Runtime;

namespace Kbx.Modules.Common.ExternalData.Status;

public sealed record Row(string DatasetId,string ProviderId,string SourceLabel,string State,long CacheEntries,DateTimeOffset? LastReceivedAt,DateTimeOffset? OldestFreshUntil,long StaleEntries,long UnavailableEntries);
public sealed record Response(IReadOnlyList<Row> Items);
file sealed record DbRow(string DatasetId,string ProviderId,string State,long CacheEntries,DateTimeOffset? LastReceivedAt,DateTimeOffset? OldestFreshUntil,long StaleEntries,long UnavailableEntries);

public sealed class Endpoint(NpgsqlDataSource dataSource):EndpointWithoutRequest<Response>
{
    public override void Configure(){Get("/api/kbx/external-data/status");Permissions("common.external-data.read");}
    public override async Task HandleAsync(CancellationToken ct)
    {
        const string sql="""
        select dataset_id as DatasetId, provider_id as ProviderId,
               case
                 when bool_or(fresh_until is not null and now() <= fresh_until) then 'fresh'
                 when bool_or(usable_until is not null and now() <= usable_until) then 'stale'
                 when bool_or(state='unavailable') then 'unavailable'
                 else 'expired'
               end as State,
               count(*) as CacheEntries,
               max(received_at) as LastReceivedAt,
               min(fresh_until) as OldestFreshUntil,
               count(*) filter(where fresh_until is not null and now()>fresh_until and usable_until is not null and now()<=usable_until) as StaleEntries,
               count(*) filter(where state='unavailable') as UnavailableEntries
          from kbx.external_data_cache
         where tenant_id=@TenantId
         group by dataset_id,provider_id
        """;
        await using var connection=await dataSource.OpenConnectionAsync(ct);
        var db=(await connection.QueryAsync<DbRow>(new CommandDefinition(sql,new{TenantId=RuntimeIdentity.TenantId(User)},cancellationToken:ct))).ToDictionary(x=>x.DatasetId,StringComparer.Ordinal);
        var rows=KbxExternalDataCatalog.All.Values.OrderBy(x=>x.Id,StringComparer.Ordinal).Select(dataset=>{
            if(db.TryGetValue(dataset.Id,out var current))return new Row(dataset.Id,dataset.ProviderId,dataset.SourceLabel,current.State,current.CacheEntries,current.LastReceivedAt,current.OldestFreshUntil,current.StaleEntries,current.UnavailableEntries);
            var state=dataset.FreshnessMode=="provider-defined" && dataset.FreshForSeconds is null ? "unavailable" : "expired";
            return new Row(dataset.Id,dataset.ProviderId,dataset.SourceLabel,state,0,null,null,0,state=="unavailable"?1:0);
        }).ToArray();
        await Send.OkAsync(new Response(rows),ct);
    }
}
