using Spectre.Console;
using Spectre.Console.Rendering;

namespace HanaTui.Tui.Components;

public sealed class LogEntry
{
    public DateTime Time  { get; init; } = DateTime.Now;
    public string Text    { get; init; } = "";
    public LogLevel Level { get; init; } = LogLevel.Info;
}

public enum LogLevel { Info, Sql, Done, Warn, Error }

/// <summary>
/// Thread-safe bounded log. Build() returns an IRenderable panel.
/// Dynamic content is rendered via Text() objects — never interpolated into markup strings.
/// </summary>
public sealed class LogPanel
{
    private const int MaxEntries   = 500;
    private const int VisibleLines = 20;

    private readonly List<LogEntry> _entries = new(MaxEntries);
    private readonly object _lock = new();

    public void Add(string text)
    {
        var level = DetectLevel(text);
        lock (_lock)
        {
            if (_entries.Count >= MaxEntries) _entries.RemoveAt(0);
            _entries.Add(new LogEntry { Time = DateTime.Now, Text = text, Level = level });
        }
    }

    public IRenderable Build()
    {
        List<LogEntry> snapshot;
        lock (_lock)
        {
            var start = Math.Max(0, _entries.Count - VisibleLines);
            snapshot = _entries.GetRange(start, _entries.Count - start);
        }

        var rows = new Grid();
        rows.AddColumn(new GridColumn().NoWrap());

        // Pad so the panel height stays stable
        for (int i = 0; i < VisibleLines - snapshot.Count; i++)
            rows.AddRow(new Text(""));

        foreach (var entry in snapshot)
        {
            var (tagLabel, tagColor, textColor) = entry.Level switch
            {
                LogLevel.Sql   => ("SQL ", Color.Blue,   Color.White),
                LogLevel.Done  => ("DONE", Color.Green,  Color.White),
                LogLevel.Warn  => ("WARN", Color.Yellow, Color.White),
                LogLevel.Error => ("ERR ", Color.Red,    Color.White),
                _              => ("INFO", Color.Grey,   Color.Silver),
            };

            // Strip the [TAG] prefix the service already prepends — we re-render it with color
            var bodyText = StripKnownPrefix(entry.Text);

            // Compose the row from Text objects — zero markup parsing of dynamic content
            var lineGrid = new Grid();
            lineGrid.AddColumn(new GridColumn().NoWrap().Width(10)); // time
            lineGrid.AddColumn(new GridColumn().NoWrap().Width(8));  // tag
            lineGrid.AddColumn(new GridColumn().NoWrap());            // body

            lineGrid.AddRow(
                new Text(entry.Time.ToString("HH:mm:ss"), new Style(Color.Grey, decoration: Decoration.Dim)),
                new Text($"[{tagLabel}]", new Style(tagColor)),
                new Text(" " + bodyText, new Style(textColor))
            );

            rows.AddRow(lineGrid);
        }

        return new Panel(rows)
        {
            Header = new PanelHeader("[bold] OPERATION LOG [/]"),
            Border = BoxBorder.Rounded,
            Padding = new Padding(1, 0),
        };
    }

    // -------------------------------------------------------------------------

    private static LogLevel DetectLevel(string text)
    {
        if (text.Contains("[SQL ]") || text.Contains("[SQL]"))   return LogLevel.Sql;
        if (text.Contains("[DONE]"))                              return LogLevel.Done;
        if (text.Contains("[WARN]"))                              return LogLevel.Warn;
        if (text.Contains("[ERROR]") || text.Contains("[ERR ]") || text.Contains("[ERR]"))
                                                                  return LogLevel.Error;
        return LogLevel.Info;
    }

    private static readonly string[] KnownPrefixes =
        ["[INFO] ", "[SQL ] ", "[SQL] ", "[DONE] ", "[WARN] ", "[ERROR] ", "[ERR ] ", "[ERR] "];

    private static string StripKnownPrefix(string text)
    {
        foreach (var p in KnownPrefixes)
            if (text.StartsWith(p, StringComparison.Ordinal))
                return text[p.Length..];
        return text;
    }
}
