@* Reference pattern for list pages: card grid (col-lg-6) + flex header + search filter + kebab dropdown + Bootstrap collapse for noisy detail + @key on iterated cards + "No X match the filter." inline + empty-state CTA. Mirror this when building new list pages. *@
@page "/admin/sites"
@using ScadaLink.Security
@using ScadaLink.Commons.Entities.Sites
@using ScadaLink.Commons.Interfaces.Repositories
@using ScadaLink.Communication
@using ScadaLink.DeploymentManager
@attribute [Authorize(Policy = AuthorizationPolicies.RequireAdmin)]
@inject ISiteRepository SiteRepository
@inject ArtifactDeploymentService ArtifactDeploymentService
@inject CommunicationService CommunicationService
@inject AuthenticationStateProvider AuthStateProvider
@inject NavigationManager NavigationManager
@inject IJSRuntime JS
@inject IDialogService Dialog
@inject Microsoft.Extensions.Logging.ILogger<Sites> Logger

<div class="container-fluid mt-3">
    <div class="d-flex justify-content-between align-items-center mb-3">
        <h4 class="mb-0">Site Management</h4>
        <div class="d-flex gap-2">
            <div class="dropdown">
                <button class="btn btn-outline-secondary btn-sm dropdown-toggle"
                        data-bs-toggle="dropdown" disabled="@_deploying">
                    @if (_deploying)
                    {
                        <span class="spinner-border spinner-border-sm me-1" role="status"></span>
                    }
                    Bulk actions
                </button>
                <ul class="dropdown-menu dropdown-menu-end">
                    <li>
                        <button class="dropdown-item" @onclick="DeployArtifactsToAllSites">
                            Deploy Artifacts to All Sites
                        </button>
                    </li>
                </ul>
            </div>
            <button class="btn btn-primary btn-sm"
                    @onclick='() => NavigationManager.NavigateTo("/admin/sites/create")'>
                + Add Site
            </button>
        </div>
    </div>

    <ToastNotification @ref="_toast" />

    @if (_loading)
    {
        <LoadingSpinner IsLoading="true" />
    }
    else if (_errorMessage != null)
    {
        <div class="alert alert-danger">@_errorMessage</div>
    }
    else if (_sites.Count == 0)
    {
        <div class="text-center py-5 text-muted">
            <p class="mb-3">No sites configured.</p>
            <button class="btn btn-primary btn-sm"
                    @onclick='() => NavigationManager.NavigateTo("/admin/sites/create")'>
                Add your first site
            </button>
        </div>
    }
    else
    {
        <div class="mb-3" style="max-width: 320px;">
            <input class="form-control form-control-sm"
                   placeholder="Filter by name or identifier…"
                   @bind="_search" @bind:event="oninput" />
        </div>

        @if (!FilteredSites.Any())
        {
            <p class="text-muted small">No sites match the filter.</p>
        }

        <div class="row g-3">
            @foreach (var site in FilteredSites)
            {
                var conns = _siteConnections.GetValueOrDefault(site.Id);
                var collapseId = $"cluster-{site.Id}";
                <div class="col-lg-6 col-12" @key="site.Id">
                    <div class="card h-100">
                        <div class="card-body">
                            <div class="d-flex justify-content-between align-items-start mb-2">
                                <div>
                                    <h5 class="card-title mb-1">@site.Name</h5>
                                    <code class="small">@site.SiteIdentifier</code>
                                </div>
                                <div class="d-flex gap-1">
                                    <button class="btn btn-outline-primary btn-sm"
                                            @onclick='() => NavigationManager.NavigateTo($"/admin/sites/{site.Id}/edit")'>
                                        Edit
                                    </button>
                                    <div class="dropdown">
                                        <button class="btn btn-outline-secondary btn-sm"
                                                data-bs-toggle="dropdown" aria-label="More actions">⋮</button>
                                        <ul class="dropdown-menu dropdown-menu-end">
                                            <li>
                                                <button class="dropdown-item"
                                                        @onclick="() => DeployArtifacts(site)"
                                                        disabled="@_deploying">
                                                    Deploy Artifacts
                                                </button>
                                            </li>
                                            <li><hr class="dropdown-divider" /></li>
                                            <li>
                                                <button class="dropdown-item text-danger"
                                                        @onclick="() => DeleteSite(site)">
                                                    Delete
                                                </button>
                                            </li>
                                        </ul>
                                    </div>
                                </div>
                            </div>

                            <p class="text-muted small mb-3">
                                @(string.IsNullOrWhiteSpace(site.Description) ? "—" : site.Description)
                            </p>

                            <div class="small text-muted mb-1">Data connections</div>
                            @if (conns is { Count: > 0 })
                            {
                                <ul class="list-unstyled mb-3">
                                    @foreach (var c in conns)
                                    {
                                        <li class="mb-1">
                                            <span class="badge bg-info text-dark me-1">@c.Protocol</span>
                                            @c.Name
                                        </li>
                                    }
                                </ul>
                            }
                            else
                            {
                                <p class="text-muted small fst-italic mb-3">No connections.</p>
                            }

                            <button class="btn btn-link btn-sm p-0 text-decoration-none"
                                    data-bs-toggle="collapse"
                                    data-bs-target="@($"#{collapseId}")"
                                    aria-expanded="false">
                                Cluster nodes (Akka, gRPC)
                            </button>
                            <div class="collapse mt-2" id="@collapseId">
                                @ClusterRow("Node A", site.NodeAAddress)
                                @ClusterRow("Node B", site.NodeBAddress)
                                @ClusterRow("gRPC A", site.GrpcNodeAAddress)
                                @ClusterRow("gRPC B", site.GrpcNodeBAddress)
                            </div>
                        </div>
                    </div>
                </div>
            }
        </div>
    }
</div>

@code {
    // CentralUI-024: delegates to the shared helper so the claim type stays
    // resolved through JwtTokenService rather than a duplicated magic string.
    private Task<string> GetCurrentUserAsync()
        => AuthStateProvider.GetCurrentUsernameAsync();

    private List<Site> _sites = new();
    private Dictionary<int, List<DataConnection>> _siteConnections = new();
    private bool _loading = true;
    private string? _errorMessage;

    private bool _deploying;
    private string _search = "";

    private ToastNotification _toast = default!;

    private IEnumerable<Site> FilteredSites =>
        string.IsNullOrWhiteSpace(_search)
            ? _sites
            : _sites.Where(s =>
                (s.Name?.Contains(_search, StringComparison.OrdinalIgnoreCase) ?? false) ||
                (s.SiteIdentifier?.Contains(_search, StringComparison.OrdinalIgnoreCase) ?? false));

    protected override async Task OnInitializedAsync()
    {
        await LoadDataAsync();
    }

    private async Task LoadDataAsync()
    {
        _loading = true;
        _errorMessage = null;
        try
        {
            _sites = (await SiteRepository.GetAllSitesAsync()).ToList();

            // CentralUI-012: fetch all data connections in one query and group
            // them by site, instead of issuing one query per site (N+1).
            _siteConnections = (await SiteRepository.GetAllDataConnectionsAsync())
                .GroupBy(c => c.SiteId)
                .ToDictionary(g => g.Key, g => g.ToList());
        }
        catch (Exception ex)
        {
            _errorMessage = $"Failed to load sites: {ex.Message}";
        }
        _loading = false;
    }

    private async Task DeleteSite(Site site)
    {
        var confirmed = await Dialog.ConfirmAsync(
            "Delete Site",
            $"Delete site '{site.Name}' ({site.SiteIdentifier})? This cannot be undone.",
            danger: true);

        if (!confirmed) return;

        try
        {
            await SiteRepository.DeleteSiteAsync(site.Id);
            await SiteRepository.SaveChangesAsync();
            CommunicationService.RefreshSiteAddresses();
            _toast.ShowSuccess($"Site '{site.Name}' deleted.");
            await LoadDataAsync();
        }
        catch (Exception ex)
        {
            _toast.ShowError($"Delete failed: {ex.Message}");
        }
    }

    private async Task DeployArtifacts(Site site)
    {
        _deploying = true;
        try
        {
            var user = await GetCurrentUserAsync();
            var result = await ArtifactDeploymentService.RetryForSiteAsync(
                site.Id, site.SiteIdentifier, user);

            if (result.IsSuccess)
                _toast.ShowSuccess($"Artifacts deployed to '{site.Name}'.");
            else
                _toast.ShowError($"Deploy to '{site.Name}' failed: {result.Error}");
        }
        catch (Exception ex)
        {
            _toast.ShowError($"Deploy to '{site.Name}' failed: {ex.Message}");
        }
        finally
        {
            _deploying = false;
        }
    }

    private async Task DeployArtifactsToAllSites()
    {
        _deploying = true;
        try
        {
            var user = await GetCurrentUserAsync();
            var result = await ArtifactDeploymentService.DeployToAllSitesAsync(user);

            if (result.IsSuccess)
            {
                var summary = result.Value!;
                _toast.ShowSuccess(
                    $"Artifacts deployed: {summary.SuccessCount} succeeded, {summary.FailureCount} failed.");
            }
            else
            {
                _toast.ShowError($"Artifact deployment failed: {result.Error}");
            }
        }
        catch (Exception ex)
        {
            _toast.ShowError($"Artifact deployment failed: {ex.Message}");
        }
        finally
        {
            _deploying = false;
        }
    }

    private RenderFragment ClusterRow(string label, string? address) => __builder =>
    {
        <div class="row g-1 align-items-center mb-1">
            <div class="col-2 small text-muted">@label</div>
            <div class="col-9">
                <code class="small d-block text-truncate" title="@address">
                    @(string.IsNullOrWhiteSpace(address) ? "—" : address)
                </code>
            </div>
            <div class="col-1 text-end">
                @if (!string.IsNullOrWhiteSpace(address))
                {
                    <button class="btn btn-link btn-sm p-0"
                            @onclick="() => CopyAsync(address!)" title="Copy">📋</button>
                }
            </div>
        </div>
    };

    private async Task CopyAsync(string text)
    {
        try
        {
            await JS.InvokeVoidAsync("navigator.clipboard.writeText", text);
            _toast.ShowSuccess("Copied to clipboard.");
        }
        catch (Microsoft.JSInterop.JSDisconnectedException)
        {
            // Circuit gone — the user has navigated away; nothing to surface.
        }
        catch (Microsoft.JSInterop.JSException ex)
        {
            // CentralUI-018: a real clipboard failure (e.g. permission denied)
            // is logged, not silently swallowed.
            Logger.LogWarning(ex, "Clipboard copy failed.");
            _toast.ShowError("Copy failed.");
        }
    }
}
