using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using CommunityToolkit.Mvvm.Messaging;
using Microsoft.Extensions.Logging;
using SharepointToolbox.Core.Messages;
using SharepointToolbox.Core.Models;
using SharepointToolbox.Localization;

namespace SharepointToolbox.ViewModels;

public abstract partial class FeatureViewModelBase : ObservableRecipient
{
    private CancellationTokenSource? _cts;
    private readonly ILogger<FeatureViewModelBase> _logger;

    [ObservableProperty]
    [NotifyCanExecuteChangedFor(nameof(CancelCommand))]
    private bool _isRunning;

    [ObservableProperty]
    private string _statusMessage = string.Empty;

    [ObservableProperty]
    private int _progressValue;

    [ObservableProperty]
    private bool _isIndeterminate;

    /// <summary>
    /// Sites selected in the global toolbar picker. Updated via GlobalSitesChangedMessage.
    /// Derived VMs check this in RunOperationAsync before falling back to per-tab SiteUrl.
    /// </summary>
    protected IReadOnlyList<SiteInfo> GlobalSites { get; private set; } = Array.Empty<SiteInfo>();

    public IAsyncRelayCommand RunCommand { get; }
    public RelayCommand CancelCommand { get; }

    protected FeatureViewModelBase(ILogger<FeatureViewModelBase> logger)
    {
        _logger = logger;
        RunCommand = new AsyncRelayCommand(ExecuteAsync, () => !IsRunning);
        CancelCommand = new RelayCommand(() => _cts?.Cancel(), () => IsRunning);
        IsActive = true; // Activates ObservableRecipient for WeakReferenceMessenger
    }

    private async Task ExecuteAsync()
    {
        _cts = new CancellationTokenSource();
        IsRunning = true;
        StatusMessage = string.Empty;
        ProgressValue = 0;
        IsIndeterminate = false;
        try
        {
            var progress = new Progress<OperationProgress>(p =>
            {
                // Indeterminate reports (throttle waits, inner scan steps) must not
                // reset the determinate bar to 0%; only update the status message
                // and flip the bar into marquee mode. The next determinate report
                // restores % and clears the marquee flag.
                if (p.IsIndeterminate)
                {
                    IsIndeterminate = true;
                }
                else
                {
                    IsIndeterminate = false;
                    ProgressValue = p.Total > 0 ? (int)(100.0 * p.Current / p.Total) : 0;
                }
                StatusMessage = p.Message;
                WeakReferenceMessenger.Default.Send(new ProgressUpdatedMessage(p));
            });
            await RunOperationAsync(_cts.Token, progress);
            // Success path: replace any lingering "Scanning X…" with a neutral
            // completion marker so stale in-progress labels don't stick around.
            StatusMessage = TranslationSource.Instance["status.complete"];
            ProgressValue = 100;
            IsIndeterminate = false;
        }
        catch (OperationCanceledException)
        {
            StatusMessage = TranslationSource.Instance["status.cancelled"];
            IsIndeterminate = false;
            _logger.LogInformation("Operation cancelled by user.");
        }
        catch (Exception ex)
        {
            StatusMessage = $"{TranslationSource.Instance["err.generic"]} {ex.Message}";
            IsIndeterminate = false;
            _logger.LogError(ex, "Operation failed.");
        }
        finally
        {
            IsRunning = false;
            _cts?.Dispose();
            _cts = null;
        }
    }

    protected abstract Task RunOperationAsync(CancellationToken ct, IProgress<OperationProgress> progress);

    protected override void OnActivated()
    {
        Messenger.Register<TenantSwitchedMessage>(this, (r, m) => ((FeatureViewModelBase)r).OnTenantSwitched(m.Value));
        Messenger.Register<GlobalSitesChangedMessage>(this, (r, m) => ((FeatureViewModelBase)r).OnGlobalSitesReceived(m.Value));
    }

    protected virtual void OnTenantSwitched(TenantProfile profile)
    {
        // Derived classes override to reset their state
    }

    private void OnGlobalSitesReceived(IReadOnlyList<SiteInfo> sites)
    {
        GlobalSites = sites;
        OnGlobalSitesChanged(sites);
    }

    /// <summary>
    /// Called when the global site selection changes. Override in derived VMs
    /// to update UI state (e.g., pre-fill SiteUrl from first global site).
    /// </summary>
    protected virtual void OnGlobalSitesChanged(IReadOnlyList<SiteInfo> sites)
    {
        // Derived classes override to react to global site changes
    }
}
