using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using CamBooth.App.Core.AppSettings;
using CamBooth.App.Core.Logging;

namespace CamBooth.App.Features.PhotoPrismUpload;

/// <summary>
/// Verwaltet die Upload-Queue für PhotoPrism und führt Uploads im Hintergrund durch.
/// </summary>
public class PhotoPrismUploadQueueService : IDisposable
{
    private readonly Logger _logger;
    private readonly AppSettingsService _appSettings;
    private readonly PhotoPrismUploadService _uploadService;
    private readonly PhotoPrismUploadTracker _uploadTracker;
    private readonly Queue<string> _uploadQueue = new();
    private readonly object _queueLock = new();

    private Task? _uploadTask;
    private CancellationTokenSource? _cancellationTokenSource;
    private bool _isRunning = false;

    private const int UploadRetryDelayMs = 5_000;
    private const int MaxRetries = 3;
    private const int CheckIntervalMs = 2_000;

    public PhotoPrismUploadQueueService(
        Logger logger,
        AppSettingsService appSettings,
        PhotoPrismUploadService uploadService)
    {
        _logger = logger;
        _appSettings = appSettings;
        _uploadService = uploadService;
        _uploadTracker = new PhotoPrismUploadTracker(logger, "PhotoPrismUploadTracking");
    }

    // =========================================================================
    // Public API
    // =========================================================================

    /// <summary>
    /// Startet den Upload-Service.
    /// </summary>
    public void Start()
    {
        if (_isRunning)
        {
            _logger.Warning("PhotoPrism Upload-Service läuft bereits");
            return;
        }

        _isRunning = true;
        _cancellationTokenSource = new CancellationTokenSource();
        _uploadTask = ProcessQueueAsync(_cancellationTokenSource.Token);
        _logger.Info("PhotoPrism Upload-Service gestartet");
    }

    /// <summary>
    /// Stoppt den Upload-Service und wartet auf den Abschluss laufender Uploads.
    /// </summary>
    public async Task StopAsync()
    {
        if (!_isRunning) return;

        _isRunning = false;
        _cancellationTokenSource?.Cancel();

        if (_uploadTask is not null)
        {
            try
            {
                await _uploadTask;
            }
            catch (OperationCanceledException)
            {
                /* erwartet */
            }
        }

        _logger.Info("PhotoPrism Upload-Service gestoppt");
    }

    /// <summary>
    /// Scannt das Bild-Verzeichnis und stellt fehlgeschlagene Uploads erneut in die Queue.
    /// </summary>
    public void ScanAndQueueFailedUploads()
    {
        if (!_appSettings.PhotoPrismAutoUploadEnabled)
        {
            _logger.Debug("PhotoPrism Auto-Upload ist deaktiviert");
            return;
        }

        try
        {
            var failedFiles = _uploadTracker.GetFailedUploads(_appSettings.PictureLocation);

            lock (_queueLock)
            {
                foreach (var filePath in failedFiles.Where(f => !_uploadQueue.Contains(f)))
                {
                    _uploadQueue.Enqueue(filePath);
                    _logger.Debug($"Fehlgeschlagenes Upload in Queue: {Path.GetFileName(filePath)}");
                }
            }

            if (failedFiles.Count > 0)
                _logger.Info($"{failedFiles.Count} fehlgeschlagene Uploads in Queue hinzugefügt");
        }
        catch (Exception ex)
        {
            _logger.Error($"Fehler beim Scannen fehlgeschlagener Uploads: {ex.Message}");
        }
    }

    /// <summary>
    /// Fügt ein neu erstelltes Foto zur Upload-Queue hinzu.
    /// </summary>
    public void QueueNewPhoto(string photoPath)
    {
        if (!_appSettings.PhotoPrismAutoUploadEnabled)
        {
            _logger.Debug("PhotoPrism Auto-Upload ist deaktiviert");
            return;
        }

        lock (_queueLock)
        {
            if (_uploadQueue.Contains(photoPath)) return;
            _uploadQueue.Enqueue(photoPath);
            _logger.Debug($"Neues Foto in PhotoPrism-Queue: {Path.GetFileName(photoPath)}");
        }
    }

    public void Dispose()
    {
        StopAsync().Wait(TimeSpan.FromSeconds(5));
        _cancellationTokenSource?.Dispose();
        _uploadService?.Dispose();
    }

    // =========================================================================
    // Private – Queue Processing
    // =========================================================================

    private async Task ProcessQueueAsync(CancellationToken cancellationToken)
    {
        _logger.Debug("PhotoPrism Upload-Queue-Verarbeitung gestartet");

        try
        {
            while (!cancellationToken.IsCancellationRequested)
            {
                string? filePath = DequeueNext();

                if (filePath is not null)
                {
                    await ProcessUploadAsync(filePath, cancellationToken);
                }
                else
                {
                    await Task.Delay(CheckIntervalMs, cancellationToken);
                }
            }
        }
        catch (OperationCanceledException)
        {
            _logger.Info("PhotoPrism Upload-Queue-Verarbeitung wurde abgebrochen");
        }
        catch (Exception ex)
        {
            _logger.Error($"Fehler in PhotoPrism Upload-Queue-Verarbeitung: {ex.Message}");
        }
    }

    private async Task ProcessUploadAsync(string filePath, CancellationToken cancellationToken)
    {
        var fileName = Path.GetFileName(filePath);

        try
        {
            if (!File.Exists(filePath))
            {
                _logger.Warning($"Bild nicht gefunden, überspringe: {fileName}");
                _uploadTracker.MarkAsFailedUpload(fileName, "Datei nicht gefunden");
                return;
            }

            if (_uploadTracker.IsUploaded(filePath))
            {
                _logger.Debug($"Bild bereits hochgeladen: {fileName}");
                return;
            }

            var (success, error) = await RetryUploadAsync(filePath, fileName, cancellationToken);

            if (success)
            {
                _uploadTracker.MarkAsUploaded(filePath);
                _logger.Debug($"✅ PhotoPrism Upload erfolgreich: {fileName}");
            }
            else
            {
                _uploadTracker.MarkAsFailedUpload(filePath, error ?? "Unbekannter Fehler");
                _logger.Error($"❌ PhotoPrism Upload fehlgeschlagen nach {MaxRetries} Versuchen: {fileName}");
                _logger.Info($"Bild wird später erneut versucht: {fileName}");
            }
        }
        catch (Exception ex)
        {
            _logger.Error($"Kritischer Fehler bei PhotoPrism Upload-Verarbeitung von {fileName}: {ex.Message}");
            _uploadTracker.MarkAsFailedUpload(filePath, ex.Message);
        }
    }

    private async Task<(bool success, string? error)> RetryUploadAsync(
        string filePath,
        string fileName,
        CancellationToken cancellationToken)
    {
        string? lastError = null;

        for (int attempt = 1; attempt <= MaxRetries; attempt++)
        {
            try
            {
                _logger.Info($"PhotoPrism Upload-Versuch {attempt}/{MaxRetries} für {fileName}...");

                var success = await _uploadService.UploadImageAsync(filePath);

                if (success)
                    return (true, null);

                lastError = "Upload-Fehler vom Service";
                _logger.Warning($"PhotoPrism Upload fehlgeschlagen (Versuch {attempt}): {fileName}");
            }
            catch (Exception ex)
            {
                lastError = ex.Message;
                _logger.Error($"Fehler bei PhotoPrism Upload-Versuch {attempt}: {ex.Message}");
            }

            if (attempt < MaxRetries)
                await Task.Delay(UploadRetryDelayMs, cancellationToken);
        }

        return (false, lastError);
    }

    private string? DequeueNext()
    {
        lock (_queueLock)
            return _uploadQueue.Count > 0 ? _uploadQueue.Dequeue() : null;
    }
}
