using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using PSLauncher.Core.Localization;
using PSLauncher.Models;

namespace PSLauncher.App.ViewModels;

public enum VersionRowState
{
    InstalledIdle,        // Installée localement, pas d'action en cours
    AvailableIdle,        // Disponible côté serveur, pas installée
    Downloading,
    Installing,
    Uninstalling,
}

public sealed partial class VersionRowViewModel : ObservableObject
{
    public string Version { get; }
    public DateTime ReleasedAt { get; }
    public string ExecutablePath { get; }
    public string FolderPath { get; }
    public long SizeBytes { get; }
    public InstalledVersion? Installed { get; }
    public VersionManifest? Remote { get; }
    public bool HasReleaseNotes => Remote?.ReleaseNotesUrl is not null;

    [ObservableProperty]
    [NotifyPropertyChangedFor(nameof(StateLabel))]
    [NotifyPropertyChangedFor(nameof(IsBusy))]
    [NotifyPropertyChangedFor(nameof(IsInstalled))]
    [NotifyPropertyChangedFor(nameof(IsRemoteOnly))]
    [NotifyPropertyChangedFor(nameof(ShowRestartFromZero))]
    [NotifyCanExecuteChangedFor(nameof(LaunchCommand))]
    [NotifyCanExecuteChangedFor(nameof(InstallCommand))]
    [NotifyCanExecuteChangedFor(nameof(UninstallCommand))]
    private VersionRowState _state;

    [ObservableProperty] private double _progressPercent;
    [ObservableProperty] private string? _progressDetail;

    [ObservableProperty]
    [NotifyPropertyChangedFor(nameof(InstallButtonLabel))]
    [NotifyPropertyChangedFor(nameof(HasResumableDownload))]
    [NotifyPropertyChangedFor(nameof(ShowRestartFromZero))]
    [NotifyCanExecuteChangedFor(nameof(RestartFromZeroCommand))]
    private long _resumableBytes;

    [ObservableProperty]
    [NotifyPropertyChangedFor(nameof(InstallButtonLabel))]
    [NotifyPropertyChangedFor(nameof(LicenseAllowsInstall))]
    [NotifyCanExecuteChangedFor(nameof(InstallCommand))]
    private bool _licenseAllowsDownload = true;

    public bool HasResumableDownload => ResumableBytes > 0;
    public bool LicenseAllowsInstall => LicenseAllowsDownload;

    /// <summary>
    /// Visibilité du bouton rouge « Annuler » à côté de « ↻ Reprendre ».
    /// Doit être vrai uniquement quand un partial existe ET qu'aucun DL n'est en
    /// cours sur cette row (sinon le bouton « Annuler » se mélange visuellement
    /// avec la barre de progression du DL actif, qui a son propre cancel).
    /// </summary>
    public bool ShowRestartFromZero => HasResumableDownload && State == VersionRowState.AvailableIdle;

    public string InstallButtonLabel
    {
        get
        {
            if (!LicenseAllowsDownload) return Strings.ActionLicenseInsufficient;
            if (!HasResumableDownload) return Strings.ActionInstall;
            if (Remote is null || Remote.Download.SizeBytes <= 0) return Strings.ResumeButtonNoPercent;
            var pct = (int)Math.Round((double)ResumableBytes / Remote.Download.SizeBytes * 100.0);
            return Strings.ResumeButton(pct);
        }
    }

    public bool IsInstalled => State is VersionRowState.InstalledIdle or VersionRowState.Uninstalling;
    public bool IsRemoteOnly => State is VersionRowState.AvailableIdle;
    public bool IsBusy => State is VersionRowState.Downloading or VersionRowState.Installing or VersionRowState.Uninstalling;

    public string StateLabel => State switch
    {
        VersionRowState.InstalledIdle => Strings.StatusInstalled,
        VersionRowState.AvailableIdle => Strings.StatusAvailable,
        VersionRowState.Downloading   => Strings.StatusDownloading,
        VersionRowState.Installing    => Strings.StatusInstalling,
        VersionRowState.Uninstalling  => Strings.StatusUninstalling,
        _ => string.Empty
    };

    public string PrimaryDate => ReleasedAt > DateTime.MinValue
        ? Strings.FormatDate(ReleasedAt)
        : "—";

    public string SizeDisplay => FormatSize(SizeBytes);

    /// <summary>Constructeur ligne « installée localement » (peut aussi être présente en remote).</summary>
    public static VersionRowViewModel ForInstalled(InstalledVersion installed, VersionManifest? remote)
    {
        return new VersionRowViewModel(
            version: installed.Version,
            releasedAt: remote?.ReleasedAt ?? installed.InstalledAt,
            executablePath: installed.ExecutablePath,
            folderPath: installed.FolderPath,
            sizeBytes: installed.SizeBytes,
            installed: installed,
            remote: remote,
            initialState: VersionRowState.InstalledIdle);
    }

    /// <summary>Constructeur ligne « disponible côté serveur uniquement ».</summary>
    public static VersionRowViewModel ForRemote(VersionManifest remote)
    {
        return new VersionRowViewModel(
            version: remote.Version,
            releasedAt: remote.ReleasedAt,
            executablePath: string.Empty,
            folderPath: string.Empty,
            sizeBytes: remote.Download.SizeBytes,
            installed: null,
            remote: remote,
            initialState: VersionRowState.AvailableIdle);
    }

    private VersionRowViewModel(
        string version, DateTime releasedAt, string executablePath, string folderPath,
        long sizeBytes, InstalledVersion? installed, VersionManifest? remote,
        VersionRowState initialState)
    {
        Version = version;
        ReleasedAt = releasedAt;
        ExecutablePath = executablePath;
        FolderPath = folderPath;
        SizeBytes = sizeBytes;
        Installed = installed;
        Remote = remote;
        _state = initialState;
    }

    // Les commandes sont câblées par le MainViewModel après instanciation
    // (les actions ont besoin de services).

    public Action<VersionRowViewModel>? LaunchHandler { get; set; }
    public Action<VersionRowViewModel>? InstallHandler { get; set; }
    public Action<VersionRowViewModel>? UninstallHandler { get; set; }
    public Action<VersionRowViewModel>? ShowReleaseNotesHandler { get; set; }
    public Action<VersionRowViewModel>? OpenFolderHandler { get; set; }
    public Action<VersionRowViewModel>? RestartFromZeroHandler { get; set; }

    [RelayCommand(CanExecute = nameof(CanLaunch))]
    private void Launch() => LaunchHandler?.Invoke(this);
    private bool CanLaunch() => State == VersionRowState.InstalledIdle;

    [RelayCommand(CanExecute = nameof(CanInstall))]
    private void Install() => InstallHandler?.Invoke(this);
    private bool CanInstall() => State == VersionRowState.AvailableIdle && LicenseAllowsDownload;

    [RelayCommand(CanExecute = nameof(CanUninstall))]
    private void Uninstall() => UninstallHandler?.Invoke(this);
    private bool CanUninstall() => State == VersionRowState.InstalledIdle;

    [RelayCommand]
    private void ShowReleaseNotes() => ShowReleaseNotesHandler?.Invoke(this);

    [RelayCommand]
    private void OpenFolder() => OpenFolderHandler?.Invoke(this);

    [RelayCommand(CanExecute = nameof(CanRestartFromZero))]
    private void RestartFromZero() => RestartFromZeroHandler?.Invoke(this);
    private bool CanRestartFromZero() => HasResumableDownload;

    private static string FormatSize(long bytes)
        => bytes <= 0 ? "—" : Strings.FormatSize(bytes);
}
