using System;
using System.IO;
using System.Windows.Input;
using MossyUpdater.Services;
using Ookii.Dialogs.Wpf;

namespace MossyUpdater.ViewModels
{
    public class MainViewModel : ViewModelBase, IDisposable
    {
        private readonly IDownloadService _downloadService;
        private readonly ISettingsService _settingsService;
        private readonly ILogger _logger;
        private readonly IQuotesService _quotesService;
        private CancellationTokenSource? _cancellationTokenSource;
        private System.Windows.Threading.DispatcherTimer? _quoteTimer;

        private string _url = "https://git.snakedesert.se/SawatoMosswartsEnjoyersClub/MosswartMassacre/raw/branch/spawn-detection/MosswartMassacre/bin/Release/MosswartMassacre.dll";
        private string _folder = string.Empty;
        private string _filename = "MosswartMassacre.dll";
        private string _statusMessage = string.Empty;
        private System.Windows.Media.Brush _statusColor = System.Windows.Media.Brushes.Green;
        private double _progressValue = 0;
        private bool _isProgressVisible = false;
        private bool _isDownloading = false;
        private string _updateStatus = string.Empty;
        private bool _updateAvailable = false;
        private string _currentQuote = string.Empty;
        private double _quoteOpacity = 1.0;

        public MainViewModel(IDownloadService downloadService, ISettingsService settingsService, ILogger logger, IQuotesService quotesService)
        {
            _downloadService = downloadService ?? throw new ArgumentNullException(nameof(downloadService));
            _settingsService = settingsService ?? throw new ArgumentNullException(nameof(settingsService));
            _logger = logger ?? throw new ArgumentNullException(nameof(logger));
            _quotesService = quotesService ?? throw new ArgumentNullException(nameof(quotesService));

            BrowseCommand = new RelayCommand(BrowseFolder);
            DownloadCommand = new AsyncRelayCommand(DownloadAndUnblock, CanDownload);
            CancelCommand = new RelayCommand(CancelDownload, () => IsDownloading);
            CheckUpdatesCommand = new AsyncRelayCommand(CheckForUpdates);

            InitializeQuoteRotation();
            _ = LoadSettingsAsync();
        }

        public string Url
        {
            get => _url;
            set => SetProperty(ref _url, value);
        }

        public string Folder
        {
            get => _folder;
            set => SetProperty(ref _folder, value);
        }

        public string Filename
        {
            get => _filename;
            set => SetProperty(ref _filename, value);
        }

        public string StatusMessage
        {
            get => _statusMessage;
            set => SetProperty(ref _statusMessage, value);
        }

        public System.Windows.Media.Brush StatusColor
        {
            get => _statusColor;
            set => SetProperty(ref _statusColor, value);
        }

        public double ProgressValue
        {
            get => _progressValue;
            set => SetProperty(ref _progressValue, value);
        }

        public bool IsProgressVisible
        {
            get => _isProgressVisible;
            set => SetProperty(ref _isProgressVisible, value);
        }

        public bool IsDownloading
        {
            get => _isDownloading;
            set
            {
                SetProperty(ref _isDownloading, value);
                OnPropertyChanged(nameof(CanInteract));
            }
        }

        public string UpdateStatus
        {
            get => _updateStatus;
            set => SetProperty(ref _updateStatus, value);
        }

        public bool UpdateAvailable
        {
            get => _updateAvailable;
            set => SetProperty(ref _updateAvailable, value);
        }

        public string CurrentQuote
        {
            get => _currentQuote;
            set => SetProperty(ref _currentQuote, value);
        }

        public double QuoteOpacity
        {
            get => _quoteOpacity;
            set => SetProperty(ref _quoteOpacity, value);
        }

        public bool CanInteract => !IsDownloading;

        public ICommand BrowseCommand { get; }
        public ICommand DownloadCommand { get; }
        public ICommand CancelCommand { get; }
        public ICommand CheckUpdatesCommand { get; }

        private void BrowseFolder()
        {
            var dialog = new VistaFolderBrowserDialog
            {
                Description = "Select a destination folder",
                UseDescriptionForTitle = true,
                SelectedPath = Folder
            };

            if (dialog.ShowDialog() == true)
            {
                Folder = dialog.SelectedPath;
                _ = SaveSettingsAsync();
            }
        }

        private bool CanDownload()
        {
            return !IsDownloading && 
                   !string.IsNullOrWhiteSpace(Url) && 
                   !string.IsNullOrWhiteSpace(Folder) && 
                   !string.IsNullOrWhiteSpace(Filename);
        }

        private async Task DownloadAndUnblock()
        {
            if (string.IsNullOrWhiteSpace(Url) || string.IsNullOrWhiteSpace(Folder) || string.IsNullOrWhiteSpace(Filename))
            {
                SetStatus("Please fill in all fields.", System.Windows.Media.Brushes.Red);
                return;
            }

            var fullPath = Path.Combine(Folder, Filename);
            _cancellationTokenSource = new CancellationTokenSource();

            try
            {
                IsDownloading = true;
                IsProgressVisible = true;
                ProgressValue = 0;

                var progress = new Progress<DownloadProgress>(OnDownloadProgress);
                var result = await _downloadService.DownloadFileAsync(Url, fullPath, progress, _cancellationTokenSource.Token);

                if (result.Success)
                {
                    _downloadService.UnblockFile(fullPath);
                    SetStatus($"Downloaded and unblocked successfully!\n{fullPath}\nSize: {FormatBytes(result.BytesDownloaded)}, Duration: {result.Duration:mm\\:ss}", System.Windows.Media.Brushes.Green);
                    ProgressValue = 100;
                    
                    await CheckForUpdates();
                }
                else if (!string.IsNullOrEmpty(result.ErrorMessage))
                {
                    SetStatus($"Download failed: {result.ErrorMessage}", System.Windows.Media.Brushes.Red);
                    ProgressValue = 0;
                }
            }
            catch (Exception ex)
            {
                SetStatus($"Unexpected error: {ex.Message}", System.Windows.Media.Brushes.Red);
                ProgressValue = 0;
            }
            finally
            {
                IsDownloading = false;
                _cancellationTokenSource?.Dispose();
                _cancellationTokenSource = null;
                
                await Task.Delay(3000);
                IsProgressVisible = false;
            }
        }

        private void CancelDownload()
        {
            _cancellationTokenSource?.Cancel();
            SetStatus("Download cancelled by user.", System.Windows.Media.Brushes.Orange);
        }

        private async Task CheckForUpdates()
        {
            if (string.IsNullOrWhiteSpace(Url) || string.IsNullOrWhiteSpace(Folder) || string.IsNullOrWhiteSpace(Filename))
            {
                UpdateStatus = "";
                UpdateAvailable = false;
                return;
            }

            var localPath = Path.Combine(Folder, Filename);
            
            try
            {
                var updateInfo = await _downloadService.CheckForUpdatesAsync(Url, localPath);
                
                if (!string.IsNullOrEmpty(updateInfo.ErrorMessage))
                {
                    UpdateStatus = $"Update check failed: {updateInfo.ErrorMessage}";
                    UpdateAvailable = false;
                }
                else if (updateInfo.UpdateAvailable)
                {
                    UpdateStatus = "Update available! Remote file is newer or different.";
                    UpdateAvailable = true;
                }
                else
                {
                    UpdateStatus = "File is up to date.";
                    UpdateAvailable = false;
                }
            }
            catch (Exception ex)
            {
                UpdateStatus = $"Update check error: {ex.Message}";
                UpdateAvailable = false;
            }
        }

        private void OnDownloadProgress(DownloadProgress progress)
        {
            ProgressValue = progress.ProgressPercentage;
            SetStatus(progress.Status, System.Windows.Media.Brushes.Blue);
        }

        private void SetStatus(string message, System.Windows.Media.Brush color)
        {
            StatusMessage = message;
            StatusColor = color;
        }

        private async Task LoadSettingsAsync()
        {
            try
            {
                var settings = await _settingsService.LoadSettingsAsync();
                Url = settings.DefaultUrl;
                Folder = settings.LastUsedFolder;
                Filename = settings.DefaultFilename;

                if (settings.CheckForUpdatesOnStartup && !string.IsNullOrWhiteSpace(Url) && !string.IsNullOrWhiteSpace(Folder) && !string.IsNullOrWhiteSpace(Filename))
                {
                    await CheckForUpdates();
                }
            }
            catch (Exception ex)
            {
                await _logger.LogErrorAsync("Failed to load settings", ex);
            }
        }

        private async Task SaveSettingsAsync()
        {
            try
            {
                var settings = new AppSettings
                {
                    DefaultUrl = Url,
                    LastUsedFolder = Folder,
                    DefaultFilename = Filename,
                    CheckForUpdatesOnStartup = true,
                    DownloadTimeoutMinutes = 10
                };

                await _settingsService.SaveSettingsAsync(settings);
            }
            catch (Exception ex)
            {
                await _logger.LogErrorAsync("Failed to save settings", ex);
            }
        }

        private static string FormatBytes(long bytes)
        {
            string[] suffixes = { "B", "KB", "MB", "GB", "TB" };
            int counter = 0;
            decimal number = bytes;
            
            while (Math.Round(number / 1024) >= 1)
            {
                number /= 1024;
                counter++;
            }
            
            return $"{number:n1} {suffixes[counter]}";
        }

        private void InitializeQuoteRotation()
        {
            CurrentQuote = _quotesService.GetNextQuote();
            
            _quoteTimer = new System.Windows.Threading.DispatcherTimer
            {
                Interval = TimeSpan.FromSeconds(5)
            };
            _quoteTimer.Tick += OnQuoteTimerTick;
            _quoteTimer.Start();
        }

        private async void OnQuoteTimerTick(object? sender, EventArgs e)
        {
            await RotateQuoteWithFade();
        }

        private async Task RotateQuoteWithFade()
        {
            const double fadeOutDuration = 300;
            const double fadeInDuration = 300;
            const int fadeSteps = 30;
            
            var fadeOutDelay = TimeSpan.FromMilliseconds(fadeOutDuration / fadeSteps);
            var fadeInDelay = TimeSpan.FromMilliseconds(fadeInDuration / fadeSteps);
            
            for (int i = fadeSteps; i >= 0; i--)
            {
                QuoteOpacity = (double)i / fadeSteps;
                await Task.Delay(fadeOutDelay);
            }
            
            CurrentQuote = _quotesService.GetNextQuote();
            
            for (int i = 0; i <= fadeSteps; i++)
            {
                QuoteOpacity = (double)i / fadeSteps;
                await Task.Delay(fadeInDelay);
            }
        }

        public void Dispose()
        {
            _quoteTimer?.Stop();
            _quoteTimer = null;
            _cancellationTokenSource?.Dispose();
        }
    }
}