using System.Collections.ObjectModel;
using ClawdDotNet.App.Models;
using ClawdDotNet.App.Services;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;

namespace ClawdDotNet.Desktop.ViewModels;

/// <summary>
/// Eine Instanz in der Auswahl.
///
/// <para>Das Monogramm und der Punkt stammen aus dem Entwurf. Der Punkt zeigt bewusst
/// <em>Bereitschaft</em> und nicht „läuft": Ob eine andere Instanz gerade läuft, weiß
/// diese Anwendung nicht — jede Instanz ist ein eigener Prozess, und einen
/// instanzübergreifenden Status gibt es noch nicht. Ein grüner Punkt für „läuft" wäre
/// hier schlicht geraten.</para>
/// </summary>
public sealed class InstanceChoiceViewModel(InstanceInfo info, bool isCurrent)
{
    public InstanceInfo Info { get; } = info;

    public string InstanceName => Info.InstanceName;
    public string FolderPath => Info.FolderPath;

    /// <summary>Erste zwei Buchstaben, gross — wie im Entwurf.</summary>
    public string Monogram => Info.InstanceName.Length >= 2
        ? Info.InstanceName[..2].ToUpperInvariant()
        : Info.InstanceName.ToUpperInvariant();

    public string Summary => $"{Info.AgentCount} Agenten · API-Key {Info.ApiKeyStatus.ToLowerInvariant()}";

    /// <summary>Ohne API-Key kann die Instanz keine Agenten starten.</summary>
    public bool IsReady => Info.ApiKeyStatus.Equals("Konfiguriert", StringComparison.OrdinalIgnoreCase);

    /// <summary>Die gerade laufende Instanz — traegt im Entwurf das Etikett „Aktiv".</summary>
    public bool IsCurrent { get; } = isCurrent;
}

/// <summary>
/// Auswahl der Instanz — beim Start und beim Wechseln aus der laufenden Anwendung.
///
/// <para>Die WinForms-Fassung mischte Datenzugriff, Spaltenformatierung und Meldungsfenster
/// in einem Formular. Hier steht nur, <em>was</em> geschieht; das <em>wie</em> es aussieht
/// liegt in der Ansicht, und Rückfragen laufen über <see cref="ConfirmError"/> statt über
/// einen direkten Dialogaufruf — so bleibt das Ansichtsmodell ohne Fensterbezug prüfbar.</para>
/// </summary>
public sealed partial class InstancePickerViewModel : ViewModelBase
{
    private readonly InstanceDirectoryManager _directories;
    private readonly TaskCompletionSource<string?> _completion = new();
    private readonly string? _currentInstancePath;

    [ObservableProperty]
    [NotifyCanExecuteChangedFor(nameof(StartCommand))]
    private InstanceChoiceViewModel? _selected;

    [ObservableProperty]
    private string _newInstanceName = "";

    /// <summary>Fehlertext für die Ansicht; leer heißt: alles in Ordnung.</summary>
    [ObservableProperty]
    private string _errorText = "";

    public ObservableCollection<InstanceChoiceViewModel> Instances { get; } = [];

    /// <summary>Der gewählte Pfad, oder <c>null</c> bei Abbruch.</summary>
    public Task<string?> Result => _completion.Task;

    public string InstancesDirectory => _directories.InstancesDirectory;

    /// <summary>
    /// Beim Start heisst die Schaltflaeche „Starten", aus der laufenden Anwendung heraus
    /// „Wechseln" — und dann steht der Hinweis dabei, dass dafuer neu gestartet wird.
    /// </summary>
    public bool IsSwitchMode => _currentInstancePath is not null;

    public string ConfirmLabel => IsSwitchMode ? "Wechseln" : "Starten";

    /// <summary>Ueberschrift des Dialogs — nicht aus ConfirmLabel zusammengesetzt,
    /// sonst stuende dort „Instanz Starten".</summary>
    public string Title => IsSwitchMode ? "Instanz wechseln" : "Instanz wählen";

    public string Hint => IsSwitchMode
        ? "Jede Instanz läuft unabhängig mit eigenen Agenten und Daten. "
          + "Wechseln startet ClawdDotNet mit der gewählten Instanz neu."
        : "Jede Instanz läuft unabhängig mit eigenen Agenten und Daten.";

    /// <param name="currentInstancePath">
    /// Pfad der laufenden Instanz, oder <c>null</c> beim Start. Steuert Beschriftung und
    /// das Etikett „Aktiv" in der Liste.
    /// </param>
    public InstancePickerViewModel(InstanceDirectoryManager directories, string? currentInstancePath = null)
    {
        _directories = directories;
        _currentInstancePath = currentInstancePath;
        Refresh();
    }

    [RelayCommand]
    private void Refresh()
    {
        Instances.Clear();

        foreach (var instance in _directories.ListInstances())
        {
            var isCurrent = _currentInstancePath is not null
                && string.Equals(instance.FolderPath, _currentInstancePath,
                    StringComparison.OrdinalIgnoreCase);

            Instances.Add(new InstanceChoiceViewModel(instance, isCurrent));
        }

        // Eine einzelne Instanz gleich vorwählen — der häufigste Fall, und ein Klick
        // weniger bei jedem Start.
        Selected ??= Instances.FirstOrDefault(i => i.IsCurrent) ?? Instances.FirstOrDefault();
    }

    private bool CanStart => Selected is not null;

    [RelayCommand(CanExecute = nameof(CanStart))]
    private void Start() => _completion.TrySetResult(Selected!.FolderPath);

    /// <summary>Waehlt eine Zeile und bestaetigt in einem Zug — die Schaltflaeche je Zeile.</summary>
    [RelayCommand]
    private void Activate(InstanceChoiceViewModel? choice)
    {
        if (choice is null) return;
        Selected = choice;
        _completion.TrySetResult(choice.FolderPath);
    }

    /// <summary>Abbruch — der Aufrufer beendet die Anwendung oder schliesst den Dialog.</summary>
    [RelayCommand]
    private void Cancel() => _completion.TrySetResult(null);

    [RelayCommand]
    private void Create()
    {
        var name = NewInstanceName.Trim();

        if (string.IsNullOrWhiteSpace(name))
        {
            ErrorText = "Bitte einen Instanznamen eingeben.";
            return;
        }

        try
        {
            _directories.CreateInstance(name);
            NewInstanceName = "";
            ErrorText = "";
            Refresh();

            // Die frisch angelegte Instanz vorwählen — wer sie erstellt, will sie starten.
            Selected = Instances.FirstOrDefault(i =>
                string.Equals(i.InstanceName, name, StringComparison.Ordinal));
        }
        catch (Exception ex) when (ex is IOException or UnauthorizedAccessException
                                       or ArgumentException)
        {
            ErrorText = $"Instanz konnte nicht angelegt werden: {ex.Message}";
        }
    }

    /// <summary>
    /// Wird gesetzt, wenn ein Fehler von außen kommt (etwa eine unlesbare
    /// Instanzkonfiguration), damit das Fenster offen bleiben und eine andere Wahl
    /// zulassen kann.
    /// </summary>
    public void ConfirmError(string message) => ErrorText = message;
}
