@page "/client-logs"
@page "/admin/client-logs"
@rendermode @(new InteractiveWebAssemblyRenderMode(prerender: false))
@attribute [Authorize]
@using Microsoft.AspNetCore.Components.Web
@inject HttpClient Http

<PageTitle>클라이언트 로그</PageTitle>

<AdminPageHeader Title="클라이언트 로그" Eyebrow="Diagnostics" Subtitle="브라우저 오류와 JS interop 실패를 최근 순서로 확인합니다.">
</AdminPageHeader>

<section class="admin-simple-page">

    <div class="admin-simple-card">
        @if (_summary is not null)
        {
            <div class="admin-log-summary">
                <div><strong>Total</strong> @_summary.Total</div>
                <div><strong>Error</strong> @_summary.ErrorCount</div>
                <div><strong>Warning</strong> @_summary.WarningCount</div>
                <div><strong>Info</strong> @_summary.InfoCount</div>
            </div>
            <div class="admin-log-summary-breakdown">
                <section>
                    <strong>Top Sources</strong>
                    <ul>
                        @foreach (var item in _summary.TopSources)
                        {
                            <li>@item.Key (@item.Value)</li>
                        }
                    </ul>
                </section>
                <section>
                    <strong>Top Routes</strong>
                    <ul>
                        @foreach (var item in _summary.TopRoutes)
                        {
                            <li>@item.Key (@item.Value)</li>
                        }
                    </ul>
                </section>
            </div>
        }
        <div class="admin-log-filter-row">
            <input class="mud-input-slot" placeholder="검색어" @bind="_query" @bind:event="oninput" />
            <select class="mud-input-slot" @bind="_level">
                <option value="">전체 수준</option>
                <option value="error">error</option>
                <option value="warning">warning</option>
                <option value="info">info</option>
            </select>
            <input class="mud-input-slot" placeholder="source" @bind="_source" @bind:event="oninput" />
            <input class="mud-input-slot" placeholder="route" @bind="_route" @bind:event="oninput" />
            <input class="mud-input-slot" placeholder="시작 ISO 시각" @bind="_start" @bind:event="oninput" />
            <input class="mud-input-slot" placeholder="종료 ISO 시각" @bind="_end" @bind:event="oninput" />
            <MudButton Variant="Variant.Filled" Color="Color.Primary" OnClick="LoadAsync">새로고침</MudButton>
        </div>
        @if (_loading)
        {
            <p>불러오는 중...</p>
        }
        else if (_entries.Count == 0)
        {
            <p>최근 클라이언트 로그가 없습니다.</p>
        }
        else
        {
            <div class="admin-log-list">
                @foreach (var entry in _entries)
                {
                    <article class="admin-log-item">
                        <div class="admin-log-item-header">
                            <strong>@(entry.Level ?? "error")</strong>
                            <span>@entry.Timestamp</span>
                            <span>@entry.Source</span>
                        </div>
                        <p>@entry.Message</p>
                        <small>@entry.Route</small>
                    </article>
                }
            </div>
        }
        @if (_groups.Count > 0)
        {
            <details class="admin-log-group-details">
                <summary>반복 오류 그룹 보기</summary>
                <div class="admin-log-group-list">
                    @foreach (var group in _groups)
                    {
                        <article class="admin-log-group-item" role="button" tabindex="0" @onclick="() => ApplyGroupFilter(group)" @onkeydown="e => HandleGroupKeydown(e, group)">
                            <div class="admin-log-item-header">
                                <strong>@group.Count</strong>
                                <span>@group.Level</span>
                                <span>@group.Source</span>
                                <span>@group.Route</span>
                            </div>
                            <p>@group.Message</p>
                            <small>@group.LastSeen</small>
                        </article>
                    }
                </div>
            </details>
        }
    </div>
</section>

@code {
    private readonly List<ClientLogDto> _entries = [];
    private readonly List<ClientLogGroupDto> _groups = [];
    private bool _loading = true;
    private string _query = "";
    private string _level = "";
    private string _source = "";
    private string _route = "";
    private string _start = "";
    private string _end = "";
    private ClientLogSummaryDto? _summary;

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

    private async Task LoadAsync()
    {
        _loading = true;
        _entries.Clear();
        _groups.Clear();

        try
        {
            _summary = await Http.GetFromJsonAsync<ClientLogSummaryDto>("/api/client-logs/summary");
            var url = $"/api/client-logs/recent?q={Uri.EscapeDataString(_query ?? string.Empty)}&level={Uri.EscapeDataString(_level ?? string.Empty)}&source={Uri.EscapeDataString(_source ?? string.Empty)}&route={Uri.EscapeDataString(_route ?? string.Empty)}&start={Uri.EscapeDataString(_start ?? string.Empty)}&end={Uri.EscapeDataString(_end ?? string.Empty)}";
            var result = await Http.GetFromJsonAsync<List<ClientLogDto>>(url);
            var grouped = await Http.GetFromJsonAsync<List<ClientLogGroupDto>>("/api/client-logs/groups");
            if (result is not null)
            {
                _entries.AddRange(result);
            }
            if (grouped is not null)
            {
                _groups.AddRange(grouped);
            }
        }
        catch
        {
        }
        finally
        {
            _loading = false;
            StateHasChanged();
        }
    }

    private async Task ApplyGroupFilter(ClientLogGroupDto group)
    {
        _query = group.Message ?? string.Empty;
        _level = group.Level ?? string.Empty;
        _source = group.Source ?? string.Empty;
        _route = group.Route ?? string.Empty;
        await LoadAsync();
    }

    private async Task HandleGroupKeydown(KeyboardEventArgs e, ClientLogGroupDto group)
    {
        if (e.Key is "Enter" or " ")
        {
            await ApplyGroupFilter(group);
        }
    }

    private sealed class ClientLogDto
    {
        public string? Timestamp { get; set; }
        public string? Level { get; set; }
        public string? Source { get; set; }
        public string? Message { get; set; }
        public string? Route { get; set; }
    }

    private sealed class ClientLogSummaryDto
    {
        public int Total { get; set; }
        public int ErrorCount { get; set; }
        public int WarningCount { get; set; }
        public int InfoCount { get; set; }
        public List<KeyValuePair<string, int>> TopSources { get; set; } = [];
        public List<KeyValuePair<string, int>> TopRoutes { get; set; } = [];
    }

    private sealed class ClientLogGroupDto
    {
        public string? Key { get; set; }
        public string? Level { get; set; }
        public string? Source { get; set; }
        public string? Route { get; set; }
        public string? Message { get; set; }
        public int Count { get; set; }
        public string? LastSeen { get; set; }
    }
}
