using System.ComponentModel;
using System.Runtime.CompilerServices;

namespace NexusInstaller.Models;

public enum InstallStepStatus
{
    Pending,
    Running,
    Completed,
    Failed,
    Blocked,
    Skipped,
    Retrying,
}

public enum InstallProgressEventKind
{
    Info,
    Warning,
    Error,
    StepStarted,
    StepCompleted,
    StepSkipped,
    StepFailed,
    StepRetrying,
    Timeout,
    FileLock,
}

public sealed class InstallProgressEvent
{
    public required DateTimeOffset Timestamp { get; init; }
    public required InstallProgressEventKind Kind { get; init; }
    public required string Message { get; init; }
    public int? StepIndex { get; init; }

    public string DisplayTime => Timestamp.LocalDateTime.ToString("HH:mm:ss");

    public string KindLabel => Kind switch
    {
        InstallProgressEventKind.Error or InstallProgressEventKind.StepFailed => "FEJL",
        InstallProgressEventKind.Warning or InstallProgressEventKind.Timeout => "ADVARSEL",
        InstallProgressEventKind.FileLock => "FIL-LÅS",
        InstallProgressEventKind.StepRetrying => "GENPRØVER",
        InstallProgressEventKind.StepCompleted => "OK",
        InstallProgressEventKind.StepSkipped => "SPRINGET",
        _ => "INFO",
    };
}

public sealed class InstallPhaseItem : INotifyPropertyChanged
{
    private InstallStepStatus _status = InstallStepStatus.Pending;
    private string? _detail;
    private DateTimeOffset? _startedAt;

    public required string Label { get; init; }
    public int Index { get; init; }

    public InstallStepStatus Status
    {
        get => _status;
        set
        {
            if (_status == value)
            {
                return;
            }

            _status = value;
            OnPropertyChanged();
            OnPropertyChanged(nameof(StatusDisplay));
            OnPropertyChanged(nameof(IsActive));
            OnPropertyChanged(nameof(IsCompleted));
            OnPropertyChanged(nameof(IsFailed));
            OnPropertyChanged(nameof(IsPending));
            OnPropertyChanged(nameof(ShowPulse));
        }
    }

    public string? Detail
    {
        get => _detail;
        set { _detail = value; OnPropertyChanged(); }
    }

    public DateTimeOffset? StartedAt
    {
        get => _startedAt;
        set { _startedAt = value; OnPropertyChanged(); OnPropertyChanged(nameof(ElapsedDisplay)); }
    }

    public bool IsActive => Status is InstallStepStatus.Running or InstallStepStatus.Retrying;
    public bool IsCompleted => Status is InstallStepStatus.Completed or InstallStepStatus.Skipped;
    public bool IsFailed => Status is InstallStepStatus.Failed or InstallStepStatus.Blocked;
    public bool IsPending => Status == InstallStepStatus.Pending;
    public bool ShowPulse => Status is InstallStepStatus.Running or InstallStepStatus.Retrying;

    public string StatusDisplay => Status switch
    {
        InstallStepStatus.Pending => "Afventer",
        InstallStepStatus.Running => "Kører",
        InstallStepStatus.Completed => "Færdig",
        InstallStepStatus.Failed => "Fejl",
        InstallStepStatus.Blocked => "Blokeret",
        InstallStepStatus.Skipped => "Sprunget over",
        InstallStepStatus.Retrying => "Genprøver",
        _ => Status.ToString(),
    };

    public string ElapsedDisplay
    {
        get
        {
            if (_startedAt is null || !IsActive)
            {
                return string.Empty;
            }

            var elapsed = DateTimeOffset.Now - _startedAt.Value;
            return elapsed.TotalSeconds < 60
                ? $"{(int)elapsed.TotalSeconds}s"
                : $"{(int)elapsed.TotalMinutes}m {elapsed.Seconds}s";
        }
    }

    public void RefreshElapsedDisplay() => OnPropertyChanged(nameof(ElapsedDisplay));

    public event PropertyChangedEventHandler? PropertyChanged;

    private void OnPropertyChanged([CallerMemberName] string? name = null) =>
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}

public static class InstallStepStatusRules
{
    public static int CountCompleted(IReadOnlyList<InstallPhaseItem> steps) =>
        steps.Count(s => s.Status is InstallStepStatus.Completed or InstallStepStatus.Skipped);

    public static int CountFailed(IReadOnlyList<InstallPhaseItem> steps) =>
        steps.Count(s => s.Status is InstallStepStatus.Failed or InstallStepStatus.Blocked);

    public static InstallPhaseItem? FindRunning(IReadOnlyList<InstallPhaseItem> steps) =>
        steps.FirstOrDefault(s => s.Status is InstallStepStatus.Running or InstallStepStatus.Retrying);

    public static double ProgressPercent(IReadOnlyList<InstallPhaseItem> steps)
    {
        if (steps.Count == 0)
        {
            return 0;
        }

        var completed = CountCompleted(steps);
        var running = steps.Any(s => s.Status is InstallStepStatus.Running or InstallStepStatus.Retrying)
            ? 0.5
            : 0;
        return Math.Clamp(((completed + running) / steps.Count) * 100.0, 0, 100);
    }

    public static string GlobalStatusPill(
        bool isInstalling,
        bool isInstalled,
        bool isHung,
        IReadOnlyList<InstallPhaseItem> steps)
    {
        if (isInstalled)
        {
            return "Færdig";
        }

        if (isHung)
        {
            return "Timeout";
        }

        if (CountFailed(steps) > 0)
        {
            return "Fejl";
        }

        if (isInstalling)
        {
            return "Installerer";
        }

        return "Klar";
    }
}
