using FastEndpoints;

namespace TaxBaik.Web.Endpoints.ClientLogs;

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

public sealed class GetSummaryEndpoint : EndpointWithoutRequest<ClientLogSummary>
{
    private readonly IWebHostEnvironment _environment;

    public GetSummaryEndpoint(IWebHostEnvironment environment)
    {
        _environment = environment;
    }

    public override void Configure()
    {
        Get("/api/client-logs/summary");
        Policies("AdminOnly");
    }

    public override async Task HandleAsync(CancellationToken ct)
    {
        var logDir = Path.Combine(_environment.ContentRootPath, "logs");
        var summary = new ClientLogSummary();
        if (!Directory.Exists(logDir))
        {
            await SendAsync(summary, cancellation: ct);
            return;
        }

        var files = Directory.GetFiles(logDir, "taxbaik-*.log")
            .OrderByDescending(File.GetLastWriteTimeUtc)
            .Take(3)
            .ToArray();

        var sourceCounts = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);
        var routeCounts = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);

        foreach (var file in files)
        {
            foreach (var line in await File.ReadAllLinesAsync(file, ct))
            {
                if (!line.Contains("ClientLog ", StringComparison.OrdinalIgnoreCase))
                    continue;

                summary.Total++;
                var level = ExtractLevel(line);
                if (string.Equals(level, "error", StringComparison.OrdinalIgnoreCase))
                    summary.ErrorCount++;
                else if (string.Equals(level, "warning", StringComparison.OrdinalIgnoreCase))
                    summary.WarningCount++;
                else
                    summary.InfoCount++;

                var source = ExtractSegment(line, "ClientLog ", 2) ?? "unknown";
                var route = ExtractNamedValue(line, "Route=") ?? "unknown";
                sourceCounts[source] = sourceCounts.TryGetValue(source, out var sourceCount) ? sourceCount + 1 : 1;
                routeCounts[route] = routeCounts.TryGetValue(route, out var routeCount) ? routeCount + 1 : 1;
            }
        }

        summary.TopSources = sourceCounts
            .OrderByDescending(kvp => kvp.Value)
            .Take(5)
            .ToList();
        summary.TopRoutes = routeCounts
            .OrderByDescending(kvp => kvp.Value)
            .Take(5)
            .ToList();

        await SendAsync(summary, cancellation: ct);
    }

    private static string? ExtractLevel(string line)
    {
        var start = line.IndexOf("ClientLog ", StringComparison.OrdinalIgnoreCase);
        if (start < 0) return null;
        var tokens = line[start..].Split(' ', StringSplitOptions.RemoveEmptyEntries);
        return tokens.Length > 1 ? tokens[1] : null;
    }

    private static string? ExtractSegment(string text, string start, int tokenIndex)
    {
        var index = text.IndexOf(start, StringComparison.OrdinalIgnoreCase);
        if (index < 0) return null;
        var tokens = text[index..].Split(' ', StringSplitOptions.RemoveEmptyEntries);
        return tokens.Length > tokenIndex ? tokens[tokenIndex] : null;
    }

    private static string? ExtractNamedValue(string text, string prefix)
    {
        var index = text.IndexOf(prefix, StringComparison.OrdinalIgnoreCase);
        if (index < 0) return null;
        var slice = text[(index + prefix.Length)..];
        var end = slice.IndexOf(' ');
        return end < 0 ? slice : slice[..end];
    }
}
