@page "/deployment/debug-view"
@using ScadaLink.Security
@using ScadaLink.Commons.Entities.Instances
@using ScadaLink.Commons.Entities.Sites
@using ScadaLink.Commons.Interfaces.Repositories
@using ScadaLink.Commons.Messages.DebugView
@using ScadaLink.Commons.Messages.Streaming
@using ScadaLink.Commons.Types
@using ScadaLink.Commons.Types.Enums
@using ScadaLink.Communication
@attribute [Authorize(Policy = AuthorizationPolicies.RequireDeployment)]
@inject ITemplateEngineRepository TemplateEngineRepository
@inject ISiteRepository SiteRepository
@inject DebugStreamService DebugStreamService
@inject IJSRuntime JS
@implements IDisposable

<div class="container-fluid mt-3">
    <h4 class="mb-3">Debug View</h4>

    <ToastNotification @ref="_toast" />

    @if (_loading)
    {
        <LoadingSpinner IsLoading="true" />
    }
    else
    {
        <div class="row mb-3 g-2">
            <div class="col-md-3">
                <label class="form-label small">Site</label>
                <select class="form-select form-select-sm" @bind="_selectedSiteId" @bind:after="LoadInstancesForSite">
                    <option value="0">Select site...</option>
                    @foreach (var site in _sites)
                    {
                        <option value="@site.Id">@site.Name (@site.SiteIdentifier)</option>
                    }
                </select>
            </div>
            <div class="col-md-4">
                <label class="form-label small">Instance</label>
                <select class="form-select form-select-sm" @bind="_selectedInstanceId" @bind:after="OnInstanceSelectionChanged">
                    <option value="0">Select instance...</option>
                    @foreach (var inst in _siteInstances)
                    {
                        <option value="@inst.Id">@inst.UniqueName (@inst.State)</option>
                    }
                </select>
            </div>
            <div class="col-md-3 d-flex align-items-end gap-2">
                @if (!_connected)
                {
                    <button class="btn btn-primary btn-sm" @onclick="Connect"
                            disabled="@(_selectedInstanceId == 0 || _selectedSiteId == 0 || _connecting)">
                        @if (_connecting) { <span class="spinner-border spinner-border-sm me-1"></span> }
                        Connect
                    </button>
                }
                else
                {
                    <button class="btn btn-outline-danger btn-sm" @onclick="Disconnect">Disconnect</button>
                    <span class="badge bg-success align-self-center">
                        <span class="spinner-grow spinner-grow-sm me-1" style="width: 0.5rem; height: 0.5rem;"></span>
                        Live
                    </span>
                }
            </div>
        </div>

        @if (_connected && _snapshot != null)
        {
            <div class="row">
                @* Attribute Values *@
                <div class="col-md-7">
                    <div class="card">
                        <div class="card-header py-2 d-flex justify-content-between">
                            <strong>Attribute Values</strong>
                            <small class="text-muted">@_attributeValues.Count values</small>
                        </div>
                        <div class="card-body p-0" style="max-height: 500px; overflow-y: auto;">
                            <table class="table table-sm table-striped mb-0">
                                <thead class="table-light sticky-top">
                                    <tr>
                                        <th>Attribute</th>
                                        <th>Value</th>
                                        <th>Quality</th>
                                        <th>Timestamp</th>
                                    </tr>
                                </thead>
                                <tbody>
                                    @foreach (var av in _attributeValues.Values.OrderBy(a => a.AttributeName))
                                    {
                                        <tr>
                                            <td class="small">@av.AttributeName</td>
                                            <td class="small font-monospace"><strong>@ValueFormatter.FormatDisplayValue(av.Value)</strong></td>
                                            <td>
                                                <span class="badge @(av.Quality == "Good" ? "bg-success" : "bg-warning text-dark")">@av.Quality</span>
                                            </td>
                                            <td class="small text-muted">@av.Timestamp.LocalDateTime.ToString("HH:mm:ss.fff")</td>
                                        </tr>
                                    }
                                </tbody>
                            </table>
                        </div>
                    </div>
                </div>

                @* Alarm States *@
                <div class="col-md-5">
                    <div class="card">
                        <div class="card-header py-2 d-flex justify-content-between">
                            <strong>Alarm States</strong>
                            <small class="text-muted">@_alarmStates.Count alarms</small>
                        </div>
                        <div class="card-body p-0" style="max-height: 500px; overflow-y: auto;">
                            <table class="table table-sm table-striped mb-0">
                                <thead class="table-light sticky-top">
                                    <tr>
                                        <th>Alarm</th>
                                        <th>State</th>
                                        <th>Priority</th>
                                        <th>Timestamp</th>
                                    </tr>
                                </thead>
                                <tbody>
                                    @foreach (var alarm in _alarmStates.Values.OrderBy(a => a.AlarmName))
                                    {
                                        <tr class="@GetAlarmRowClass(alarm.State)">
                                            <td class="small">@alarm.AlarmName</td>
                                            <td>
                                                <span class="badge @GetAlarmStateBadge(alarm.State)">@alarm.State</span>
                                            </td>
                                            <td class="small">@alarm.Priority</td>
                                            <td class="small text-muted">@alarm.Timestamp.LocalDateTime.ToString("HH:mm:ss.fff")</td>
                                        </tr>
                                    }
                                </tbody>
                            </table>
                        </div>
                    </div>
                </div>
            </div>

            <div class="text-muted small mt-2">
                Snapshot received: @_snapshot.SnapshotTimestamp.LocalDateTime.ToString("HH:mm:ss") |
                @_attributeValues.Count attributes, @_alarmStates.Count alarms
            </div>
        }
        else if (_connected)
        {
            <LoadingSpinner IsLoading="true" Message="Waiting for snapshot..." />
        }
    }
</div>

@code {
    private List<Site> _sites = new();
    private List<Instance> _siteInstances = new();
    private int _selectedSiteId;
    private int _selectedInstanceId;
    private bool _loading = true;
    private bool _connected;
    private bool _connecting;

    private DebugViewSnapshot? _snapshot;
    private Dictionary<string, AttributeValueChanged> _attributeValues = new();
    private Dictionary<string, AlarmStateChanged> _alarmStates = new();

    private DebugStreamSession? _session;
    private ToastNotification _toast = default!;

    protected override async Task OnInitializedAsync()
    {
        try
        {
            _sites = (await SiteRepository.GetAllSitesAsync()).ToList();
        }
        catch (Exception ex)
        {
            _toast.ShowError($"Failed to load sites: {ex.Message}");
        }
        _loading = false;
    }

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (!firstRender) return;

        var storedSiteId = await JS.InvokeAsync<string>("localStorage.getItem", "debugView.siteId");
        var storedInstanceId = await JS.InvokeAsync<string>("localStorage.getItem", "debugView.instanceId");

        if (!string.IsNullOrEmpty(storedSiteId) && int.TryParse(storedSiteId, out var siteId)
            && !string.IsNullOrEmpty(storedInstanceId) && int.TryParse(storedInstanceId, out var instanceId))
        {
            _selectedSiteId = siteId;
            await LoadInstancesForSite();
            _selectedInstanceId = instanceId;
            StateHasChanged();
            await Connect();
        }
    }

    private async Task LoadInstancesForSite()
    {
        _siteInstances.Clear();
        _selectedInstanceId = 0;
        if (_selectedSiteId == 0) return;
        try
        {
            _siteInstances = (await TemplateEngineRepository.GetInstancesBySiteIdAsync(_selectedSiteId))
                .Where(i => i.State == InstanceState.Enabled)
                .ToList();
        }
        catch (Exception ex)
        {
            _toast.ShowError($"Failed to load instances: {ex.Message}");
        }
    }

    private void OnInstanceSelectionChanged()
    {
        // No-op; selection is tracked via _selectedInstanceId binding
    }

    private async Task Connect()
    {
        if (_selectedInstanceId == 0 || _selectedSiteId == 0) return;
        _connecting = true;
        try
        {
            var session = await DebugStreamService.StartStreamAsync(
                _selectedInstanceId,
                onEvent: evt =>
                {
                    switch (evt)
                    {
                        case AttributeValueChanged av:
                            _attributeValues[av.AttributeName] = av;
                            _ = InvokeAsync(StateHasChanged);
                            break;
                        case AlarmStateChanged al:
                            _alarmStates[al.AlarmName] = al;
                            _ = InvokeAsync(StateHasChanged);
                            break;
                    }
                },
                onTerminated: () =>
                {
                    _connected = false;
                    _session = null;
                    _ = InvokeAsync(() =>
                    {
                        _toast.ShowError("Debug stream terminated (site disconnected).");
                        StateHasChanged();
                    });
                });

            _session = session;

            // Populate initial state from snapshot
            _attributeValues.Clear();
            foreach (var av in session.InitialSnapshot.AttributeValues)
                _attributeValues[av.AttributeName] = av;

            _alarmStates.Clear();
            foreach (var al in session.InitialSnapshot.AlarmStates)
                _alarmStates[al.AlarmName] = al;

            _snapshot = session.InitialSnapshot;
            _connected = true;

            // Persist selection to localStorage for auto-reconnect on refresh
            await JS.InvokeVoidAsync("localStorage.setItem", "debugView.siteId", _selectedSiteId.ToString());
            await JS.InvokeVoidAsync("localStorage.setItem", "debugView.instanceId", _selectedInstanceId.ToString());

            var instance = _siteInstances.FirstOrDefault(i => i.Id == _selectedInstanceId);
            _toast.ShowSuccess($"Streaming {instance?.UniqueName ?? "instance"}");
        }
        catch (Exception ex)
        {
            _toast.ShowError($"Connect failed: {ex.Message}");
        }
        _connecting = false;
    }

    private async Task Disconnect()
    {
        if (_session != null)
        {
            DebugStreamService.StopStream(_session.SessionId);
            _session = null;
        }

        // Clear persisted selection — user explicitly disconnected
        await JS.InvokeVoidAsync("localStorage.removeItem", "debugView.siteId");
        await JS.InvokeVoidAsync("localStorage.removeItem", "debugView.instanceId");

        _connected = false;
        _snapshot = null;
        _attributeValues.Clear();
        _alarmStates.Clear();
    }

    private static string GetAlarmStateBadge(AlarmState state) => state switch
    {
        AlarmState.Active => "bg-danger",
        AlarmState.Normal => "bg-success",
        _ => "bg-secondary"
    };

    private static string GetAlarmRowClass(AlarmState state) => state switch
    {
        AlarmState.Active => "table-danger",
        _ => ""
    };

    public void Dispose()
    {
        if (_session != null)
        {
            DebugStreamService.StopStream(_session.SessionId);
        }
    }
}
