@using EntKube.Web.Data
@using EntKube.Web.Services
@inject AppGovernanceService GovernanceService
@inject TenantService TenantService

@* ═══════════════════════════════════════════════════════════════════
   GovernanceTab — tenant admin view for app-level namespace, resource
   quotas, network policies, and RBAC. All changes are saved to the
   database. "Apply to clusters" pushes to every cluster where this
   app has a deployment.
   ═══════════════════════════════════════════════════════════════════ *@

<div class="d-flex align-items-center justify-content-between mb-3">
    <div>
        <h5 class="mb-0"><i class="bi bi-shield-check me-2 text-primary"></i>Governance</h5>
        <small class="text-muted">Namespace, quotas, network policies, and RBAC — read-only in the customer portal.</small>
    </div>
    <button class="btn btn-sm btn-success" @onclick="ApplyToAllClusters" disabled="@(applying || string.IsNullOrWhiteSpace(ns))">
        @if (applying) { <span class="spinner-border spinner-border-sm me-1"></span> }
        else { <i class="bi bi-cloud-upload me-1"></i> }
        Apply to clusters
    </button>
</div>

@if (applyOutput is not null)
{
    <div class="alert @(applySuccess ? "alert-success" : "alert-danger") py-2 small mb-3 d-flex align-items-start justify-content-between">
        <div>
            <i class="bi @(applySuccess ? "bi-check-circle" : "bi-exclamation-triangle") me-1"></i>
            <strong>@(applySuccess ? "Applied successfully" : "Apply failed")</strong>
            <pre class="mb-0 mt-1 small" style="white-space:pre-wrap;max-height:200px;overflow-y:auto;">@applyOutput</pre>
        </div>
        <button class="btn-close btn-close-sm ms-2 flex-shrink-0" @onclick="() => applyOutput = null"></button>
    </div>
}

@if (loading)
{
    <div class="text-center py-4"><div class="spinner-border spinner-border-sm text-primary"></div></div>
}
else
{
    @* ── 1. Namespace ─────────────────────────────────────────────── *@
    <div class="card shadow-sm mb-3">
        <div class="card-header bg-white py-2">
            <i class="bi bi-box me-2 text-primary"></i>
            <strong>Namespace</strong>
            <span class="text-muted small ms-2">The Kubernetes namespace where quotas and policies are enforced.</span>
        </div>
        <div class="card-body">
            <div class="row g-2 align-items-end">
                <div class="col-md-5">
                    <label class="form-label small">Namespace</label>
                    <input class="form-control form-control-sm font-monospace" @bind="ns"
                           placeholder="e.g. my-app" />
                    <div class="form-text">Must be a valid DNS label (lowercase, hyphens, max 63 chars).</div>
                </div>
                <div class="col-auto">
                    <button class="btn btn-sm btn-primary" @onclick="SaveNamespace"
                            disabled="@savingNs">
                        @if (savingNs) { <span class="spinner-border spinner-border-sm me-1"></span> }
                        Save
                    </button>
                </div>
                @if (nsMessage is not null)
                {
                    <div class="col-auto">
                        <span class="small @(nsOk ? "text-success" : "text-danger")">
                            <i class="bi @(nsOk ? "bi-check-circle" : "bi-x-circle") me-1"></i>@nsMessage
                        </span>
                    </div>
                }
            </div>
        </div>
    </div>

    @* ── 2. Resource Quota ─────────────────────────────────────────── *@
    <div class="card shadow-sm mb-3">
        <div class="card-header bg-white py-2 d-flex align-items-center justify-content-between">
            <div>
                <i class="bi bi-speedometer me-2 text-warning"></i>
                <strong>Resource Quota</strong>
                <span class="text-muted small ms-2">Limits applied as a Kubernetes ResourceQuota in the namespace.</span>
            </div>
            @if (quota is not null)
            {
                <button class="btn btn-sm btn-outline-danger" @onclick="DeleteQuota">
                    <i class="bi bi-trash me-1"></i>Remove quota
                </button>
            }
        </div>
        <div class="card-body">
            @if (quotaMessage is not null)
            {
                <div class="alert @(quotaOk ? "alert-success" : "alert-danger") py-1 small mb-2">
                    <i class="bi @(quotaOk ? "bi-check-circle" : "bi-x-circle") me-1"></i>@quotaMessage
                </div>
            }
            <div class="row g-2 mb-2">
                <div class="col-md-3">
                    <label class="form-label small">CPU request</label>
                    <input class="form-control form-control-sm font-monospace" @bind="qCpuReq" placeholder="e.g. 250m" />
                </div>
                <div class="col-md-3">
                    <label class="form-label small">CPU limit</label>
                    <input class="form-control form-control-sm font-monospace" @bind="qCpuLim" placeholder="e.g. 2" />
                </div>
                <div class="col-md-3">
                    <label class="form-label small">Memory request</label>
                    <input class="form-control form-control-sm font-monospace" @bind="qMemReq" placeholder="e.g. 128Mi" />
                </div>
                <div class="col-md-3">
                    <label class="form-label small">Memory limit</label>
                    <input class="form-control form-control-sm font-monospace" @bind="qMemLim" placeholder="e.g. 512Mi" />
                </div>
                <div class="col-md-3">
                    <label class="form-label small">Max pods</label>
                    <input type="number" class="form-control form-control-sm" @bind="qMaxPods" placeholder="e.g. 10" min="1" />
                </div>
                <div class="col-md-3">
                    <label class="form-label small">Max PVCs</label>
                    <input type="number" class="form-control form-control-sm" @bind="qMaxPvcs" placeholder="e.g. 5" min="1" />
                </div>
            </div>
            <button class="btn btn-sm btn-primary" @onclick="SaveQuota" disabled="@savingQuota">
                @if (savingQuota) { <span class="spinner-border spinner-border-sm me-1"></span> }
                Save quota
            </button>
        </div>
    </div>

    @* ── 3. Network Policies ───────────────────────────────────────── *@
    <div class="card shadow-sm mb-3">
        <div class="card-header bg-white py-2 d-flex align-items-center justify-content-between">
            <div>
                <i class="bi bi-diagram-2 me-2 text-info"></i>
                <strong>Network Policies</strong>
                <span class="text-muted small ms-2">Applied as Kubernetes NetworkPolicy resources.</span>
            </div>
            <button class="btn btn-sm btn-outline-info" @onclick="() => showAddNp = !showAddNp">
                <i class="bi bi-plus me-1"></i>Add policy
            </button>
        </div>
        <div class="card-body">
            @if (npMessage is not null)
            {
                <div class="alert alert-danger py-1 small mb-2">@npMessage</div>
            }

            @if (showAddNp)
            {
                <div class="card border-info mb-3">
                    <div class="card-body">
                        <div class="row g-2 mb-2">
                            <div class="col-md-4">
                                <label class="form-label small">Policy name (K8s resource name)</label>
                                <input class="form-control form-control-sm font-monospace" @bind="newNpName"
                                       placeholder="e.g. deny-all" />
                            </div>
                            <div class="col-md-4">
                                <label class="form-label small">Type</label>
                                <select class="form-select form-select-sm" @bind="newNpType">
                                    <option value="@AppNetworkPolicyType.DenyAll">Deny all (ingress + egress)</option>
                                    <option value="@AppNetworkPolicyType.AllowFromIngress">Allow from ingress controller</option>
                                    <option value="@AppNetworkPolicyType.AllowFromSameNamespace">Allow from same namespace</option>
                                    <option value="@AppNetworkPolicyType.AllowFromNamespace">Allow from specific namespace</option>
                                    <option value="@AppNetworkPolicyType.Custom">Custom YAML</option>
                                </select>
                            </div>
                            @if (newNpType == AppNetworkPolicyType.AllowFromNamespace)
                            {
                                <div class="col-md-4">
                                    <label class="form-label small">Source namespace</label>
                                    <input class="form-control form-control-sm font-monospace" @bind="newNpAllowNs"
                                           placeholder="e.g. monitoring" />
                                </div>
                            }
                        </div>
                        @if (newNpType == AppNetworkPolicyType.Custom)
                        {
                            <div class="mb-2">
                                <label class="form-label small">Custom NetworkPolicy YAML</label>
                                <textarea class="form-control form-control-sm font-monospace" rows="8"
                                          @bind="newNpCustomYaml"
                                          placeholder="apiVersion: networking.k8s.io/v1&#10;kind: NetworkPolicy&#10;..." />
                            </div>
                        }
                        <div class="d-flex gap-1">
                            <button class="btn btn-sm btn-info" @onclick="AddNetworkPolicy"
                                    disabled="@(string.IsNullOrWhiteSpace(newNpName) || addingNp)">
                                @if (addingNp) { <span class="spinner-border spinner-border-sm me-1"></span> }
                                Add
                            </button>
                            <button class="btn btn-sm btn-outline-secondary" @onclick="() => showAddNp = false">Cancel</button>
                        </div>
                    </div>
                </div>
            }

            @if (networkPolicies.Count == 0 && !showAddNp)
            {
                <div class="text-center py-3">
                    <i class="bi bi-diagram-2 text-muted" style="font-size: 1.5rem;"></i>
                    <p class="text-muted small mt-2 mb-0">No network policies. The namespace inherits the cluster's defaults.</p>
                </div>
            }
            else if (networkPolicies.Count > 0)
            {
                <table class="table table-sm mb-0">
                    <thead class="table-light">
                        <tr>
                            <th class="small">Name</th>
                            <th class="small">Type</th>
                            <th class="small">Detail</th>
                            <th></th>
                        </tr>
                    </thead>
                    <tbody>
                        @foreach (AppNetworkPolicy np in networkPolicies)
                        {
                            <tr>
                                <td class="small align-middle"><code>@np.Name</code></td>
                                <td class="small align-middle">@NpTypeLabel(np.PolicyType)</td>
                                <td class="small align-middle text-muted">
                                    @if (np.PolicyType == AppNetworkPolicyType.AllowFromNamespace)
                                    {
                                        <code>@np.AllowFromNamespace</code>
                                    }
                                    else if (np.PolicyType == AppNetworkPolicyType.Custom)
                                    {
                                        <span>Custom YAML</span>
                                    }
                                </td>
                                <td class="text-end align-middle">
                                    @if (confirmDeleteNpId != np.Id)
                                    {
                                        <button class="btn btn-sm btn-outline-danger"
                                                @onclick="() => confirmDeleteNpId = np.Id">
                                            <i class="bi bi-trash"></i>
                                        </button>
                                    }
                                    else
                                    {
                                        <button class="btn btn-sm btn-danger" @onclick="() => DeleteNetworkPolicy(np.Id)">Yes</button>
                                        <button class="btn btn-sm btn-outline-secondary ms-1" @onclick="() => confirmDeleteNpId = null">No</button>
                                    }
                                </td>
                            </tr>
                        }
                    </tbody>
                </table>
            }
        </div>
    </div>

    @* ── 4. RBAC ───────────────────────────────────────────────────── *@
    <div class="card shadow-sm">
        <div class="card-header bg-white py-2 d-flex align-items-center justify-content-between">
            <div>
                <i class="bi bi-person-badge me-2 text-success"></i>
                <strong>RBAC</strong>
                <span class="text-muted small ms-2">ServiceAccount, Role, and RoleBinding applied to the namespace.</span>
            </div>
        </div>
        <div class="card-body">
            @if (rbacMessage is not null)
            {
                <div class="alert @(rbacOk ? "alert-success" : "alert-danger") py-1 small mb-2">
                    <i class="bi @(rbacOk ? "bi-check-circle" : "bi-x-circle") me-1"></i>@rbacMessage
                </div>
            }

            @* Service account settings *@
            <div class="row g-2 mb-3 align-items-end">
                <div class="col-md-4">
                    <label class="form-label small">Service account name</label>
                    <input class="form-control form-control-sm font-monospace" @bind="rbacSaName"
                           placeholder="e.g. my-app" />
                </div>
                <div class="col-auto d-flex align-items-center gap-2" style="padding-bottom: 0.25rem;">
                    <div class="form-check mb-0">
                        <input class="form-check-input" type="checkbox" id="rbacAutoMount" @bind="rbacAutoMount" />
                        <label class="form-check-label small" for="rbacAutoMount">Auto-mount token</label>
                    </div>
                </div>
                <div class="col-auto">
                    <button class="btn btn-sm btn-success" @onclick="SaveRbac"
                            disabled="@(string.IsNullOrWhiteSpace(rbacSaName) || savingRbac)">
                        @if (savingRbac) { <span class="spinner-border spinner-border-sm me-1"></span> }
                        Save
                    </button>
                    @if (rbacPolicy is not null)
                    {
                        <button class="btn btn-sm btn-outline-danger ms-1" @onclick="DeleteRbac">Remove RBAC</button>
                    }
                </div>
            </div>

            @* Rules table *@
            @if (rbacPolicy is not null)
            {
                <div class="mb-2 d-flex align-items-center justify-content-between">
                    <span class="small fw-medium text-muted">Role rules</span>
                    <div class="d-flex gap-1">
                        <button class="btn btn-sm btn-outline-secondary" @onclick='() => AddPreset("", "pods,services,configmaps,secrets", "get,list,watch")'>
                            + Read namespace
                        </button>
                        <button class="btn btn-sm btn-outline-secondary" @onclick='() => AddPreset("apps", "deployments", "get,list,watch,patch,update")'>
                            + Manage deployments
                        </button>
                        <button class="btn btn-sm btn-outline-primary" @onclick="() => showAddRule = !showAddRule">
                            <i class="bi bi-plus"></i>Custom
                        </button>
                    </div>
                </div>

                @if (showAddRule)
                {
                    <div class="card border-success mb-2">
                        <div class="card-body py-2">
                            <div class="row g-2 mb-2">
                                <div class="col-md-3">
                                    <label class="form-label small">API groups</label>
                                    <input class="form-control form-control-sm font-monospace" @bind="newRuleApiGroups"
                                           placeholder='e.g. "" or "apps"' />
                                </div>
                                <div class="col-md-4">
                                    <label class="form-label small">Resources</label>
                                    <input class="form-control form-control-sm font-monospace" @bind="newRuleResources"
                                           placeholder="e.g. pods,services" />
                                </div>
                                <div class="col-md-4">
                                    <label class="form-label small">Verbs</label>
                                    <input class="form-control form-control-sm font-monospace" @bind="newRuleVerbs"
                                           placeholder="e.g. get,list,watch" />
                                </div>
                            </div>
                            <div class="d-flex gap-1">
                                <button class="btn btn-sm btn-success" @onclick="AddRbacRule"
                                        disabled="@(string.IsNullOrWhiteSpace(newRuleResources) || string.IsNullOrWhiteSpace(newRuleVerbs))">
                                    Add rule
                                </button>
                                <button class="btn btn-sm btn-outline-secondary" @onclick="() => showAddRule = false">Cancel</button>
                            </div>
                        </div>
                    </div>
                }

                @if (rbacPolicy.Rules.Count == 0)
                {
                    <p class="text-muted small mb-0">No rules yet. Add preset rules or a custom rule above.</p>
                }
                else
                {
                    <table class="table table-sm mb-0">
                        <thead class="table-light">
                            <tr>
                                <th class="small">API groups</th>
                                <th class="small">Resources</th>
                                <th class="small">Verbs</th>
                                <th></th>
                            </tr>
                        </thead>
                        <tbody>
                            @foreach (AppRbacRule rule in rbacPolicy.Rules)
                            {
                                <tr>
                                    <td class="small align-middle"><code>@(string.IsNullOrEmpty(rule.ApiGroups) ? "\"\"" : rule.ApiGroups)</code></td>
                                    <td class="small align-middle"><code>@rule.Resources</code></td>
                                    <td class="small align-middle"><code>@rule.Verbs</code></td>
                                    <td class="text-end align-middle">
                                        <button class="btn btn-sm btn-outline-danger p-0 px-1"
                                                @onclick="() => DeleteRbacRule(rule.Id)">
                                            <i class="bi bi-x" style="font-size:.8rem;"></i>
                                        </button>
                                    </td>
                                </tr>
                            }
                        </tbody>
                    </table>
                }
            }
        </div>
    </div>
}

@code {
    [Parameter, EditorRequired] public Guid AppId { get; set; }
    [Parameter, EditorRequired] public Guid TenantId { get; set; }

    private bool loading = true;

    // Namespace
    private string ns = "";
    private bool savingNs;
    private string? nsMessage;
    private bool nsOk;

    // Quota
    private AppQuota? quota;
    private string qCpuReq = "", qCpuLim = "", qMemReq = "", qMemLim = "";
    private int? qMaxPods, qMaxPvcs;
    private bool savingQuota;
    private string? quotaMessage;
    private bool quotaOk;

    // Network policies
    private List<AppNetworkPolicy> networkPolicies = [];
    private bool showAddNp;
    private bool addingNp;
    private string newNpName = "";
    private AppNetworkPolicyType newNpType = AppNetworkPolicyType.DenyAll;
    private string newNpAllowNs = "";
    private string newNpCustomYaml = "";
    private Guid? confirmDeleteNpId;
    private string? npMessage;

    // RBAC
    private AppRbacPolicy? rbacPolicy;
    private string rbacSaName = "";
    private bool rbacAutoMount;
    private bool savingRbac;
    private bool showAddRule;
    private string newRuleApiGroups = "";
    private string newRuleResources = "";
    private string newRuleVerbs = "";
    private string? rbacMessage;
    private bool rbacOk;

    // Apply
    private bool applying;
    private string? applyOutput;
    private bool applySuccess;

    protected override async Task OnInitializedAsync()
    {
        await LoadAsync();
        loading = false;
    }

    private async Task LoadAsync()
    {
        AppGovernanceData data = await GovernanceService.LoadAsync(AppId);
        ns = data.Namespace ?? "";

        quota = data.Quota;
        if (quota is not null)
        {
            qCpuReq = quota.CpuRequest ?? "";
            qCpuLim = quota.CpuLimit ?? "";
            qMemReq = quota.MemoryRequest ?? "";
            qMemLim = quota.MemoryLimit ?? "";
            qMaxPods = quota.MaxPods;
            qMaxPvcs = quota.MaxPvcs;
        }

        networkPolicies = data.NetworkPolicies;

        rbacPolicy = data.RbacPolicy;
        if (rbacPolicy is not null)
        {
            rbacSaName = rbacPolicy.ServiceAccountName;
            rbacAutoMount = rbacPolicy.AutoMountToken;
        }
    }

    private async Task SaveNamespace()
    {
        savingNs = true; nsMessage = null;
        await GovernanceService.SetNamespaceAsync(AppId, ns);
        savingNs = false; nsOk = true;
        nsMessage = "Saved.";
    }

    private async Task SaveQuota()
    {
        savingQuota = true; quotaMessage = null;
        quota = await GovernanceService.SaveQuotaAsync(AppId,
            qCpuReq, qCpuLim, qMemReq, qMemLim, qMaxPods, qMaxPvcs);
        savingQuota = false; quotaOk = true;
        quotaMessage = "Quota saved.";
    }

    private async Task DeleteQuota()
    {
        await GovernanceService.DeleteQuotaAsync(AppId);
        quota = null;
        qCpuReq = qCpuLim = qMemReq = qMemLim = "";
        qMaxPods = qMaxPvcs = null;
    }

    private async Task AddNetworkPolicy()
    {
        addingNp = true; npMessage = null;
        try
        {
            AppNetworkPolicy p = await GovernanceService.AddNetworkPolicyAsync(
                AppId, newNpName, newNpType,
                newNpAllowNs, newNpCustomYaml);
            networkPolicies.Add(p);
            showAddNp = false;
            newNpName = ""; newNpAllowNs = ""; newNpCustomYaml = "";
            newNpType = AppNetworkPolicyType.DenyAll;
        }
        catch (Exception ex) { npMessage = ex.Message; }
        finally { addingNp = false; }
    }

    private async Task DeleteNetworkPolicy(Guid id)
    {
        await GovernanceService.DeleteNetworkPolicyAsync(id);
        networkPolicies.RemoveAll(p => p.Id == id);
        confirmDeleteNpId = null;
    }

    private async Task SaveRbac()
    {
        savingRbac = true; rbacMessage = null;
        rbacPolicy = await GovernanceService.SaveRbacPolicyAsync(AppId, rbacSaName, rbacAutoMount);
        savingRbac = false; rbacOk = true;
        rbacMessage = "Saved.";
    }

    private async Task DeleteRbac()
    {
        await GovernanceService.DeleteRbacPolicyAsync(AppId);
        rbacPolicy = null; rbacSaName = ""; rbacAutoMount = false;
    }

    private async Task AddPreset(string apiGroups, string resources, string verbs)
    {
        if (rbacPolicy is null) return;
        AppRbacRule rule = await GovernanceService.AddRbacRuleAsync(
            rbacPolicy.Id, apiGroups, resources, verbs);
        rbacPolicy.Rules.Add(rule);
    }

    private async Task AddRbacRule()
    {
        if (rbacPolicy is null) return;
        AppRbacRule rule = await GovernanceService.AddRbacRuleAsync(
            rbacPolicy.Id, newRuleApiGroups, newRuleResources, newRuleVerbs);
        rbacPolicy.Rules.Add(rule);
        showAddRule = false;
        newRuleApiGroups = newRuleResources = newRuleVerbs = "";
    }

    private async Task DeleteRbacRule(Guid id)
    {
        await GovernanceService.DeleteRbacRuleAsync(id);
        rbacPolicy?.Rules.Remove(rbacPolicy.Rules.First(r => r.Id == id));
    }

    private async Task ApplyToAllClusters()
    {
        applying = true; applyOutput = null;
        var sb = new System.Text.StringBuilder();

        List<KubernetesCluster> clusters = await TenantService.GetClustersAsync(TenantId);

        foreach (KubernetesCluster cluster in clusters)
        {
            var (ok, output) = await GovernanceService.ApplyToClusterAsync(AppId, cluster);
            sb.AppendLine($"── {cluster.Name} ──");
            sb.AppendLine(ok ? "✓ Applied" : $"✗ {output}");
            if (!ok && !string.IsNullOrWhiteSpace(output))
                sb.AppendLine(output);
            applySuccess = ok;
        }

        applying = false;
        applyOutput = sb.ToString().TrimEnd();
        applySuccess = !applyOutput.Contains("✗");
    }

    private static string NpTypeLabel(AppNetworkPolicyType t) => t switch
    {
        AppNetworkPolicyType.DenyAll               => "Deny all",
        AppNetworkPolicyType.AllowFromIngress       => "Allow from ingress",
        AppNetworkPolicyType.AllowFromSameNamespace => "Allow same namespace",
        AppNetworkPolicyType.AllowFromNamespace     => "Allow from namespace",
        AppNetworkPolicyType.Custom                 => "Custom YAML",
        _                                           => t.ToString()
    };
}
