using ReactiveUI;
using SPTInstaller.CustomControls;
using SPTInstaller.Interfaces;
using System.Threading.Tasks;
using System.Windows.Input;

namespace SPTInstaller.Models;

public abstract class PreCheckBase : ReactiveObject, IPreCheck
{
    public event EventHandler ReeevaluationRequested = delegate { };
    private string _id;
    
    public string Id
    {
        get => _id;
        set => this.RaiseAndSetIfChanged(ref _id, value);
    }
    
    private bool _isSelected;
    
    public bool IsSelected
    {
        get => _isSelected;
        set => this.RaiseAndSetIfChanged(ref _isSelected, value);
    }
    
    private string _name;
    
    public string Name
    {
        get => _name;
        set => this.RaiseAndSetIfChanged(ref _name, value);
    }
    
    private bool _required;
    
    public bool IsRequired
    {
        get => _required;
        set => this.RaiseAndSetIfChanged(ref _required, value);
    }
    
    private StatusSpinner.SpinnerState _state;
    
    public StatusSpinner.SpinnerState State
    {
        get => _state;
        set => this.RaiseAndSetIfChanged(ref _state, value);
    }
    
    private string _preCheckDetails;
    
    public string PreCheckDetails
    {
        get => _preCheckDetails;
        set => this.RaiseAndSetIfChanged(ref _preCheckDetails, value);
    }
    
    private bool _actionButtonIsVisible;
    
    public bool ActionButtonIsVisible
    {
        get => _actionButtonIsVisible;
        set => this.RaiseAndSetIfChanged(ref _actionButtonIsVisible, value);
    }
    
    private string _actionButtonText;
    
    public string ActionButtonText
    {
        get => _actionButtonText;
        set => this.RaiseAndSetIfChanged(ref _actionButtonText, value);
    }
    
    private ICommand _actionButtonCommand;
    
    public ICommand ActionButtonCommand
    {
        get => _actionButtonCommand;
        set => this.RaiseAndSetIfChanged(ref _actionButtonCommand, value);
    }
    
    /// <summary>
    /// Base class for pre-checks to run before installation
    /// </summary>
    /// <param name="name">The display name of the pre-check</param>
    /// <param name="required">If installation should stop on failing this pre-check</param>
    public PreCheckBase(string name, bool required)
    {
        Name = name;
        IsRequired = required;
        Id = Guid.NewGuid().ToString();
    }
    
    private StatusSpinner.SpinnerState ProcessResult(PreCheckResult result) =>
        (result.Succeeded, IsRequired) switch
        {
            (true, _) => StatusSpinner.SpinnerState.OK,
            (false, false) => StatusSpinner.SpinnerState.Warning,
            (_, _) => StatusSpinner.SpinnerState.Error
        };
    
    /// <summary>
    /// Request pre-checks to be re-evaluated
    /// </summary>
    public void RequestReevaluation()
    {
        ReeevaluationRequested?.Invoke(this, null);
    }
    
    public async Task<IResult> RunCheck()
    {
        State = StatusSpinner.SpinnerState.Running;
        
        var result = await CheckOperation();
        
        PreCheckDetails = !string.IsNullOrWhiteSpace(result.Message)
            ? result.Message
            : (result.Succeeded
                ? "Pre-Check succeeded, but no details were provided"
                : "Pre-Check failed, but no details were provided");
        
        ActionButtonText = result.ActionButtonText;
        ActionButtonCommand = result.ButtonPressedCommand;
        ActionButtonIsVisible = result.ActionButtonIsVisible;
        
        State = ProcessResult(result);
        
        return State == StatusSpinner.SpinnerState.OK
            ? Result.FromSuccess()
            : Result.FromError($"PreCheck Failed: {Name}");
    }
    
    public abstract Task<PreCheckResult> CheckOperation();
}