@page "/portal/status"
@using EntKube.Web.Data
@using EntKube.Web.Services
@using Microsoft.EntityFrameworkCore
@using Microsoft.AspNetCore.Authorization
@using Microsoft.AspNetCore.Components.Authorization
@attribute [Authorize]
@rendermode InteractiveServer
@inject CustomerAccessService CustomerAccessService
@inject IncidentService IncidentService
@inject IDbContextFactory<ApplicationDbContext> DbFactory
@inject AuthenticationStateProvider AuthStateProvider
@inject NavigationManager Navigation

<PageTitle>Status Overview</PageTitle>

<div class="d-flex justify-content-between align-items-center mb-3">
    <div>
        <h4 class="mb-0"><i class="bi bi-activity me-2"></i>Status Overview</h4>
        <div class="text-muted small">Health and uptime across all your applications</div>
    </div>
    <button class="btn btn-sm btn-outline-secondary" @onclick="LoadData" disabled="@isLoading">
        <span class="bi bi-arrow-clockwise @(isLoading ? "spin" : "")"></span> Refresh
    </button>
</div>

<LoadingPanel Loading="@isLoading" LoadingText="Loading status…">
@if (!isLoading && rows.Count == 0)
{
    <EmptyState Icon="bi-shield-lock"
                Title="No applications to show"
                Message="You may not have access to any customer yet." />
}
else
{
    <div class="card shadow-sm">
        <div class="table-responsive">
            <table class="table table-hover align-middle mb-0">
                <thead class="table-light">
                    <tr>
                        <th>Application</th>
                        <th>Customer</th>
                        <th>Health</th>
                        <th>Uptime (7d)</th>
                        <th>Uptime (30d)</th>
                        <th>SLA Target</th>
                        <th>Active Alerts</th>
                        <th>Last Sync</th>
                    </tr>
                </thead>
                <tbody>
                    @foreach (StatusRow row in rows)
                    {
                        <tr style="cursor:pointer" @onclick="() => GoToPortal(row)">
                            <td class="fw-semibold">@row.AppName</td>
                            <td class="text-muted">@row.CustomerName</td>
                            <td>@HealthBadge(row.WorstHealth)</td>
                            <td>
                                @if (row.Uptime7d.HasValue)
                                {
                                    <span class="fw-semibold @UptimeColor(row.Uptime7d.Value, row.SlaTarget)">@row.Uptime7d.Value.ToString("F2")%</span>
                                }
                                else
                                {
                                    <span class="text-muted">—</span>
                                }
                            </td>
                            <td>
                                @if (row.Uptime30d.HasValue)
                                {
                                    <span class="fw-semibold @UptimeColor(row.Uptime30d.Value, row.SlaTarget)">@row.Uptime30d.Value.ToString("F2")%</span>
                                }
                                else
                                {
                                    <span class="text-muted">—</span>
                                }
                            </td>
                            <td class="text-muted small">
                                @if (row.SlaTarget.HasValue)
                                {
                                    bool met30 = !row.Uptime30d.HasValue || row.Uptime30d.Value >= row.SlaTarget.Value;
                                    <span class="@(met30 ? "text-success" : "text-danger")">
                                        <i class="bi bi-@(met30 ? "check" : "exclamation-triangle") me-1"></i>@row.SlaTarget.Value.ToString("F1")%
                                    </span>
                                }
                                else
                                {
                                    <span class="text-muted">—</span>
                                }
                            </td>
                            <td>
                                @if (row.ActiveAlerts > 0)
                                {
                                    <span class="badge bg-danger">@row.ActiveAlerts</span>
                                }
                                else
                                {
                                    <span class="text-success small"><i class="bi bi-check-circle me-1"></i>None</span>
                                }
                            </td>
                            <td class="text-muted small">
                                @(row.LastSync.HasValue ? row.LastSync.Value.ToLocalTime().ToString("MMM d HH:mm") : "—")
                            </td>
                        </tr>
                    }
                </tbody>
            </table>
        </div>
    </div>

    <div class="text-muted small mt-2 text-end">
        Refreshed @lastRefreshed.ToLocalTime().ToString("HH:mm:ss") · Health snapshots every 5 minutes
    </div>
}
</LoadingPanel>

@code {
    private List<StatusRow> rows = [];
    private bool isLoading = true;
    private DateTime lastRefreshed;
    private string? currentUserId;

    protected override async Task OnInitializedAsync()
    {
        AuthenticationState authState = await AuthStateProvider.GetAuthenticationStateAsync();
        currentUserId = authState.User.FindFirst(System.Security.Claims.ClaimTypes.NameIdentifier)?.Value;
        await LoadData();
    }

    private async Task LoadData()
    {
        if (currentUserId is null) return;

        isLoading = true;
        StateHasChanged();

        List<Customer> customers = await CustomerAccessService.GetAccessibleCustomersAsync(currentUserId);

        using ApplicationDbContext db = DbFactory.CreateDbContext();

        List<Guid> appIds = customers.SelectMany(c => c.Apps).Select(a => a.Id).ToList();

        // Load all deployments for these apps
        List<AppDeployment> deployments = await db.AppDeployments
            .Where(d => appIds.Contains(d.AppId))
            .ToListAsync();

        List<Guid> deploymentIds = deployments.Select(d => d.Id).ToList();

        // Load uptime data for last 30 days
        DateTime from30 = DateTime.UtcNow.AddDays(-30);
        DateTime from7 = DateTime.UtcNow.AddDays(-7);

        var snapshots = await db.DeploymentHealthSnapshots
            .Where(s => deploymentIds.Contains(s.DeploymentId) && s.SnapshotAt >= from30)
            .Select(s => new { s.DeploymentId, s.HealthStatus, s.SnapshotAt })
            .ToListAsync();

        // Load active incidents for these clusters, fetching LabelsJson for namespace matching
        List<Guid> clusterIds = deployments.Select(d => d.ClusterId).Distinct().ToList();
        List<(Guid ClusterId, string LabelsJson)> activeIncidentLabels = await db.AlertIncidents
            .Where(i => clusterIds.Contains(i.ClusterId)
                     && (i.Status == IncidentStatus.Active || i.Status == IncidentStatus.Acknowledged))
            .Select(i => new ValueTuple<Guid, string>(i.ClusterId, i.LabelsJson))
            .ToListAsync();

        // Build rows per app
        rows = [];
        foreach (Customer customer in customers.OrderBy(c => c.Name))
        {
            foreach (EntKube.Web.Data.App app in customer.Apps.OrderBy(a => a.Name))
            {
                List<AppDeployment> appDeployments = deployments.Where(d => d.AppId == app.Id).ToList();
                if (appDeployments.Count == 0) continue;

                HealthStatus worstHealth = appDeployments
                    .Select(d => d.HealthStatus)
                    .OrderByDescending(h => (int)h)
                    .FirstOrDefault();

                DateTime? lastSync = appDeployments
                    .Where(d => d.LastSyncedAt.HasValue)
                    .Max(d => d.LastSyncedAt);

                // Compute uptime per deployment then average
                double? uptime7d = null, uptime30d = null;
                foreach (AppDeployment dep in appDeployments)
                {
                    var depSnaps = snapshots.Where(s => s.DeploymentId == dep.Id).ToList();
                    if (depSnaps.Count == 0) continue;

                    var snaps7 = depSnaps.Where(s => s.SnapshotAt >= from7).ToList();
                    var snaps30 = depSnaps;

                    if (snaps7.Count > 0)
                    {
                        double pct = (double)snaps7.Count(s => s.HealthStatus == HealthStatus.Healthy) / snaps7.Count * 100;
                        uptime7d = uptime7d.HasValue ? (uptime7d + pct) / 2 : pct;
                    }
                    if (snaps30.Count > 0)
                    {
                        double pct = (double)snaps30.Count(s => s.HealthStatus == HealthStatus.Healthy) / snaps30.Count * 100;
                        uptime30d = uptime30d.HasValue ? (uptime30d + pct) / 2 : pct;
                    }
                }

                // Count alerts by matching namespace label in LabelsJson
                HashSet<Guid> appClusterIds = appDeployments.Select(d => d.ClusterId).ToHashSet();
                HashSet<string> appNamespaces = appDeployments.Select(d => d.Namespace).ToHashSet();
                int activeAlerts = activeIncidentLabels
                    .Where(x => appClusterIds.Contains(x.ClusterId))
                    .Count(x =>
                    {
                        try
                        {
                            using var doc = System.Text.Json.JsonDocument.Parse(x.LabelsJson);
                            return doc.RootElement.TryGetProperty("namespace", out var ns)
                                   && appNamespaces.Contains(ns.GetString() ?? "");
                        }
                        catch { return false; }
                    });

                // Look up SLA target (app > customer > none)
                SlaTarget? sla = await IncidentService.GetSlaTargetAsync(
                    customer.TenantId, customer.Id, app.Id);

                rows.Add(new StatusRow(
                    app.Name,
                    customer.Name,
                    customer.Id,
                    worstHealth,
                    uptime7d.HasValue ? Math.Round(uptime7d.Value, 2) : null,
                    uptime30d.HasValue ? Math.Round(uptime30d.Value, 2) : null,
                    sla?.TargetPercent,
                    activeAlerts,
                    lastSync));
            }
        }

        lastRefreshed = DateTime.UtcNow;
        isLoading = false;
    }

    private void GoToPortal(StatusRow row) =>
        Navigation.NavigateTo("/portal");

    private static string UptimeColor(double pct, double? slaTarget)
    {
        double threshold = slaTarget ?? 99.0;
        if (pct >= threshold) return "text-success";
        if (pct >= threshold - 2) return "text-warning";
        return "text-danger";
    }

    private static RenderFragment HealthBadge(HealthStatus health) => health switch
    {
        HealthStatus.Healthy    => @<span class="badge bg-success">Healthy</span>,
        HealthStatus.Progressing => @<span class="badge bg-primary">Progressing</span>,
        HealthStatus.Degraded   => @<span class="badge bg-warning text-dark">Degraded</span>,
        HealthStatus.Suspended  => @<span class="badge bg-secondary">Suspended</span>,
        HealthStatus.Missing    => @<span class="badge bg-danger">Missing</span>,
        _ => @<span class="badge bg-secondary">Unknown</span>
    };

    private record StatusRow(
        string AppName,
        string CustomerName,
        Guid CustomerId,
        HealthStatus WorstHealth,
        double? Uptime7d,
        double? Uptime30d,
        double? SlaTarget,
        int ActiveAlerts,
        DateTime? LastSync);
}
