using Dapper;
using Npgsql;

namespace Kbx.Shared.Telemetry;

public sealed class KbxUxMetricsQuery(NpgsqlDataSource dataSource)
{
    public async Task<KbxUxMetricsResponse> ExecuteAsync(Guid tenantId,DateOnly from,DateOnly to,string? screenId,CancellationToken ct)
    {
        await using var connection=await dataSource.OpenConnectionAsync(ct);
        var args=new{TenantId=tenantId,From=from.ToDateTime(TimeOnly.MinValue,DateTimeKind.Utc),To=to.AddDays(1).ToDateTime(TimeOnly.MinValue,DateTimeKind.Utc),ScreenId=screenId};
        const string eventsSql="""
        select event_name as EventName,count(*) as Samples,
               percentile_cont(0.5) within group(order by duration_ms) filter(where duration_ms is not null) as P50,
               percentile_cont(0.95) within group(order by duration_ms) filter(where duration_ms is not null) as P95
          from kbx.ux_events
         where tenant_id=@TenantId and occurred_at>=@From and occurred_at<@To
           and (@ScreenId is null or screen_id=@ScreenId)
         group by event_name;
        """;
        var rows=(await connection.QueryAsync<EventAgg>(new CommandDefinition(eventsSql,args,cancellationToken:ct))).ToDictionary(x=>x.EventName,StringComparer.Ordinal);
        const string outcomeSql="""
        select coalesce(sum(observed_count),0) Observed,coalesce(sum(manual_intervention_count),0) Manual
          from kbx.ux_business_outcomes where tenant_id=@TenantId and business_date>=date(@From) and business_date<date(@To) and (@ScreenId is null or screen_id=@ScreenId);
        """;
        var outcome=await connection.QuerySingleAsync<OutcomeAgg>(new CommandDefinition(outcomeSql,args,cancellationToken:ct));
        long Count(string name)=>rows.TryGetValue(name,out var x)?x.Samples:0;
        double? Ratio(long n,long d)=>d==0?null:Math.Round((double)n*100d/d,2);
        var metrics=new List<KbxUxMetricRow>{
            new("task_completion_time_ms","Task Completion Time",rows.GetValueOrDefault("task.complete")?.P50,"ms",Count("task.complete"),rows.GetValueOrDefault("task.complete")?.P50,rows.GetValueOrDefault("task.complete")?.P95),
            new("semantic_interactions_per_task","Interactions / Task",Count("task.complete")==0?null:Math.Round((double)Count("interaction.execute")/Count("task.complete"),2),"count",Count("task.complete")),
            new("manual_intervention_rate","Manual Intervention Rate",outcome.Observed==0?null:Math.Round((double)outcome.Manual*100d/outcome.Observed,2),"percent",outcome.Observed),
            new("validation_failure_rate","Validation Failure Rate",Ratio(Count("validation.failed"),Count("command.execute")),"percent",Count("command.execute")),
            new("import_failure_rate","Import Failure Rate",Ratio(Count("excel.import.failed"),Count("excel.import.start")),"percent",Count("excel.import.start")),
            new("exception_resolution_time_ms","Exception Resolution Time",rows.GetValueOrDefault("exception.resolved")?.P50,"ms",Count("exception.resolved"),rows.GetValueOrDefault("exception.resolved")?.P50,rows.GetValueOrDefault("exception.resolved")?.P95),
            new("ai_proposal_acceptance_rate","AI Proposal Acceptance Rate",Ratio(Count("ai.proposal.accept"),Count("ai.proposal.open")),"percent",Count("ai.proposal.open")),
        };
        return new(from,to,screenId,metrics);
    }
    private sealed record EventAgg(string EventName,long Samples,double? P50,double? P95);
    private sealed record OutcomeAgg(long Observed,long Manual);
}
