@page "/notes"
@rendermode @(new InteractiveWebAssemblyRenderMode(prerender: false))
@using Freman.Sample.Web.Contracts
@using Freman.Sample.Web.Client.Components
@inject HttpClient Http

<PageTitle>Notes</PageTitle>

<h1>Notes</h1>

<p class="text-muted">
    Client-side UI (WASM) calling server API at <code>/api/notes</code>.
</p>

<ConfirmModal @ref="_confirmModal"/>

@if (!string.IsNullOrWhiteSpace(_error))
{
    <div class="alert alert-danger" role="alert">@_error</div>
}

<EditForm Model="_model" OnValidSubmit="SaveAsync">
    <DataAnnotationsValidator/>
    <ValidationSummary/>

    <div class="mb-3">
        <label class="form-label" for="noteText">New note</label>
        <input id="noteText"
               class="form-control"
               @bind-value="_model.Text"
               @bind-value:event="oninput"
               maxlength="500"
               disabled="@(_isSaving || _isLoading)"/>
        <div class="form-text">Max 500 characters.</div>
    </div>

    <button class="btn btn-primary" type="submit" disabled="@(_isSaving || _isLoading)">
        @if (_isSaving)
        {
            <span class="spinner-border spinner-border-sm me-2" role="status" aria-hidden="true"></span>
            <span>Saving…</span>
        }
        else
        {
            <span>Save</span>
        }
    </button>

    <button class="btn btn-outline-secondary ms-2" type="button" @onclick="LoadAsync"
            disabled="@(_isSaving || _isLoading)">
        Refresh
    </button>
</EditForm>

<hr/>

@if (_isLoading)
{
    <div class="d-flex align-items-center gap-2">
        <div class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></div>
        <span>Loading…</span>
    </div>
}
else if (_notes.Count == 0)
{
    <p><em>No notes yet.</em></p>
}
else
{
    <ul class="list-group">
        @foreach (var n in _notes)
        {
            <li class="list-group-item d-flex justify-content-between align-items-center">
                <div class="me-3">
                    <div>@n.Text</div>
                    <small class="text-muted">@n.CreatedUtc.ToLocalTime()</small>
                </div>

                <button class="btn btn-sm btn-outline-danger"
                        type="button"
                        title="Delete note"
                        disabled="@(_isSaving || _isLoading || _isDeletingIds.Contains(n.Id))"
                        @onclick="() => DeleteAsync(n)">
                    @if (_isDeletingIds.Contains(n.Id))
                    {
                        <span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span>
                    }
                    else
                    {
                        <span>Delete</span>
                    }
                </button>
            </li>
        }
    </ul>
}

@code {

    private readonly CreateNoteRequest _model = new();
    private List<NoteDto> _notes = [];
    private bool _isLoading = true;
    private bool _isSaving;
    private string? _error;

    private readonly HashSet<int> _isDeletingIds = [];

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

    private async Task LoadAsync()
    {
        _isLoading = true;
        _error = null;

        try
        {
            _notes = await Http.GetFromJsonAsync<List<NoteDto>>("/api/notes") ?? [];
        }
        catch (Exception ex)
        {
            _error = $"Load failed: {ex.Message}";
        }
        finally
        {
            _isLoading = false;
        }
    }

    private async Task SaveAsync()
    {
        _isSaving = true;
        _error = null;

        try
        {
            var resp = await Http.PostAsJsonAsync("/api/notes", new { text = _model.Text });
            if (!resp.IsSuccessStatusCode)
            {
                var msg = await resp.Content.ReadAsStringAsync();
                _error = string.IsNullOrWhiteSpace(msg) ? $"Save failed: {resp.StatusCode}" : msg;
                return;
            }

            _model.Text = "";
            await LoadAsync();
        }
        catch (Exception ex)
        {
            _error = $"Save failed: {ex.Message}";
        }
        finally
        {
            _isSaving = false;
        }
    }

    private ConfirmModal? _confirmModal;

    private async Task DeleteAsync(NoteDto note)
    {
        if (_confirmModal is null)
        {
            _error = "Confirm modal not available.";
            return;
        }

        var ok = await _confirmModal.ShowAsync(
            title: "Delete note?",
            message: $"Are you sure you want to delete:\n\n\"{note.Text}\"",
            confirmText: "Delete",
            cancelText: "Cancel");

        if (!ok)
            return;

        if (!_isDeletingIds.Add(note.Id))
            return;

        _error = null;

        var index = _notes.FindIndex(n => n.Id == note.Id);
        if (index < 0)
        {
            _isDeletingIds.Remove(note.Id);
            return;
        }

        // Optimistic UI: remove immediately
        _notes.RemoveAt(index);

        try
        {
            var resp = await Http.DeleteAsync($"/api/notes/{note.Id}");
            if (!resp.IsSuccessStatusCode)
            {
                // Roll back optimistic delete
                _notes.Insert(index, note);

                var msg = await resp.Content.ReadAsStringAsync();
                _error = string.IsNullOrWhiteSpace(msg) ? $"Delete failed: {resp.StatusCode}" : msg;
            }
        }
        catch (Exception ex)
        {
            // Roll back optimistic delete
            _notes.Insert(index, note);
            _error = $"Delete failed: {ex.Message}";
        }
        finally
        {
            _isDeletingIds.Remove(note.Id);
        }
    }
}