using System.Collections.ObjectModel;
using Avalonia.Threading;
using ClawdDotNet.App;
using ClawdDotNet.App.Services;
using ClawdDotNet.Core.Storage;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;

namespace ClawdDotNet.Desktop.ViewModels;

/// <summary>
/// Live-Ansicht der Logdateien.
///
/// Der Takt läuft über <see cref="DispatcherTimer"/> — das Avalonia-Gegenstück zum
/// <c>System.Windows.Forms.Timer</c>, also auf dem Oberflächenfaden. Für eine Ansicht
/// ist das genau richtig: Das Lesen selbst dauert Millisekunden, und so entfällt jedes
/// Marshalling. Alles, was ohne Fenster laufen muss, liegt dagegen in
/// <see cref="LogTail"/> und taktet dort über <c>PeriodicTimer</c>.
/// </summary>
public sealed partial class LogPageViewModel : PageViewModel
{
    /// <summary>
    /// Obergrenze der angezeigten Zeilen.
    ///
    /// Ohne Deckel wächst die Liste unbegrenzt — ein Agent, der eine Nacht durchläuft,
    /// hinterlässt sonst Hunderttausende Einträge im Speicher, und das Scrollen wird zäh.
    /// </summary>
    private const int MaxLines = 2000;

    private readonly LogTail? _tail;
    private readonly DispatcherTimer? _timer;
    private readonly string _logDirectory;

    public ObservableCollection<LogLine> Lines { get; } = [];

    public ObservableCollection<string> Modules { get; } = ["Alle"];

    public IReadOnlyList<string> Levels { get; } = ["Alle", "Info", "Warn", "Error"];

    [ObservableProperty]
    private string _selectedModule = "Alle";

    [ObservableProperty]
    private string _selectedLevel = "Alle";

    /// <summary>
    /// Hängt die Ansicht am unteren Ende? Sobald jemand nach oben scrollt, soll nicht
    /// weitergesprungen werden — sonst kann man nichts lesen, während etwas läuft.
    /// </summary>
    [ObservableProperty]
    private bool _followTail = true;

    public LogPageViewModel(AppHost? host) : base("Logs")
    {
        _logDirectory = host?.LogDirectory ?? "";

        if (host is null) return; // Entwurfsmodus

        _tail = new LogTail(_logDirectory);
        RefreshModules();

        _timer = new DispatcherTimer { Interval = TimeSpan.FromMilliseconds(500) };
        _timer.Tick += (_, _) => Poll();
        _timer.Start();
    }

    private void Poll()
    {
        if (_tail is null) return;

        var module = SelectedModule == "Alle" ? null : SelectedModule;
        var level = SelectedLevel switch
        {
            "Info" => LogLevelFilter.Info,
            "Warn" => LogLevelFilter.Warn,
            "Error" => LogLevelFilter.Error,
            _ => LogLevelFilter.All
        };

        foreach (var line in _tail.ReadNew(module, level))
        {
            Lines.Add(line);

            // Von vorn kürzen statt am Ende zu deckeln: Das Neueste soll bleiben.
            while (Lines.Count > MaxLines)
                Lines.RemoveAt(0);
        }
    }

    [RelayCommand]
    private void Clear() => Lines.Clear();

    [RelayCommand]
    private void RefreshModules()
    {
        if (_tail is null) return;

        var current = SelectedModule;

        Modules.Clear();
        Modules.Add("Alle");

        foreach (var module in _tail.AvailableModules())
            Modules.Add(module);

        // Auswahl halten, wenn es das Modul noch gibt.
        SelectedModule = Modules.Contains(current) ? current : "Alle";
    }

    [RelayCommand]
    private void OpenFolder() => SystemShell.OpenFolder(_logDirectory);
}
