using System.Collections.ObjectModel;
using Avalonia.Threading;
using ClawdDotNet.App;
using ClawdDotNet.App.Settings;
using ClawdDotNet.Core.Backup;
using ClawdDotNet.Core.Storage;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using Microsoft.Extensions.Logging;

namespace ClawdDotNet.Desktop.ViewModels;

public sealed record BackupItemViewModel(
    string FileName,
    DateTime LastWriteTime,
    string InstanceName,
    long SizeBytes,
    string SizeFormatted,
    bool HasSecrets,
    string SecretsInfo,
    string FullPath,
    bool IsReadable
);

/// <summary>
/// Ansichtsmodell für die Sicherungs- und Wiederherstellungsseite.
/// </summary>
public sealed partial class BackupPageViewModel : PageViewModel
{
    private readonly BackupService _service = new();
    private readonly AppHost? _host;
    private readonly SettingsManager? _settingsManager;
    private readonly ILogger? _logger;
    private readonly string _instanceDir;
    private readonly string _instanceName;
    private bool _loading;

    [ObservableProperty]
    private string _targetFolder = "";

    [ObservableProperty]
    private bool _secretsExclude = true;

    [ObservableProperty]
    private bool _secretsPassphrase;

    [ObservableProperty]
    private string _passphrase = "";

    [ObservableProperty]
    private string _passphraseRepeat = "";

    [ObservableProperty]
    private bool _includeLogs;

    [ObservableProperty]
    private bool _includeChatHistory = true;

    [ObservableProperty]
    private bool _autoBackupEnabled;

    [ObservableProperty]
    private string _autoBackupTime = "03:00";

    [ObservableProperty]
    private int _keepCount = 10;

    [ObservableProperty]
    private string _statusText = "Bereit";

    [ObservableProperty]
    private bool _isBusy;

    [ObservableProperty]
    private BackupItemViewModel? _selectedBackup;

    public ObservableCollection<BackupItemViewModel> Backups { get; } = [];

    public event Func<RestoreBackupViewModel, Task<RestoreResultData?>>? RequestRestoreDialog;

    public BackupPageViewModel() : this(null) { }

    public BackupPageViewModel(AppHost? host) : base("Sicherung")
    {
        _host = host;

        if (host is not null)
        {
            _instanceDir = host.InstancePath;
            _instanceName = host.Instance.InstanceName;
            _settingsManager = host.Settings;
            _logger = host.LoggerFactory.CreateLogger("ClawdDotNet.Desktop.Backup");

            LoadSettings();
            RefreshBackupList();
        }
        else
        {
            _instanceDir = "";
            _instanceName = "Entwurf";
            TargetFolder = @"C:\ClawdDotNet\Backups";
        }
    }

    partial void OnAutoBackupEnabledChanged(bool value) => SaveSettings();
    partial void OnAutoBackupTimeChanged(string value) => SaveSettings();
    partial void OnKeepCountChanged(int value) => SaveSettings();

    private void LoadSettings()
    {
        if (_settingsManager is null) return;

        _loading = true;
        try
        {
            var settings = _settingsManager.AppSettings;
            TargetFolder = Path.GetFullPath(settings.BackupDirectory);
            AutoBackupEnabled = settings.AutoBackupEnabled;
            KeepCount = Math.Clamp(settings.BackupKeepCount, 0, 999);

            AutoBackupTime = TimeSpan.TryParse(settings.AutoBackupTime, out var time)
                ? time.ToString(@"hh\:mm")
                : "03:00";
        }
        finally
        {
            _loading = false;
        }
    }

    public void SaveSettings()
    {
        if (_loading || _settingsManager is null) return;

        var settings = _settingsManager.AppSettings;
        settings.BackupDirectory = TargetFolder.Trim();
        settings.AutoBackupEnabled = AutoBackupEnabled;
        settings.AutoBackupTime = AutoBackupTime.Trim();
        settings.BackupKeepCount = KeepCount;

        _settingsManager.Save();
    }

    [RelayCommand]
    private void RefreshBackupList()
    {
        Backups.Clear();
        var folder = TargetFolder.Trim();

        if (string.IsNullOrWhiteSpace(folder) || !Directory.Exists(folder))
            return;

        Task.Run(async () =>
        {
            var files = new DirectoryInfo(folder)
                .GetFiles("*.zip")
                .OrderByDescending(f => f.LastWriteTime)
                .ToList();

            var list = new List<BackupItemViewModel>();

            foreach (var file in files)
            {
                try
                {
                    var manifest = await _service.InspectAsync(file.FullName);
                    var secretsInfo = manifest.HasSecrets
                        ? $"ja ({manifest.SecretCount}, Passphrase)"
                        : "nein";

                    list.Add(new BackupItemViewModel(
                        file.Name, file.LastWriteTime, manifest.InstanceName,
                        file.Length, FormatSize(file.Length), manifest.HasSecrets, secretsInfo,
                        file.FullName, true));
                }
                catch
                {
                    list.Add(new BackupItemViewModel(
                        file.Name, file.LastWriteTime, "—",
                        file.Length, FormatSize(file.Length), false, "unlesbar",
                        file.FullName, false));
                }
            }

            await Dispatcher.UIThread.InvokeAsync(() =>
            {
                Backups.Clear();
                foreach (var item in list)
                    Backups.Add(item);
            });
        });
    }

    [RelayCommand]
    private async Task CreateBackupAsync()
    {
        if (string.IsNullOrWhiteSpace(_instanceDir))
        {
            StatusText = "Keine gültige Instanz geladen.";
            return;
        }

        var folder = TargetFolder.Trim();
        if (string.IsNullOrWhiteSpace(folder))
        {
            StatusText = "Bitte einen Zielordner angeben.";
            return;
        }

        BackupOptions options;
        if (SecretsPassphrase)
        {
            if (string.IsNullOrEmpty(Passphrase))
            {
                StatusText = "Bitte eine Passphrase eingeben.";
                return;
            }

            if (Passphrase != PassphraseRepeat)
            {
                StatusText = "Die Passphrasen stimmen nicht überein.";
                return;
            }

            options = new BackupOptions
            {
                Secrets = SecretMode.Passphrase,
                Passphrase = Passphrase,
                IncludeLogs = IncludeLogs,
                IncludeChatHistory = IncludeChatHistory
            };
        }
        else
        {
            options = new BackupOptions
            {
                Secrets = SecretMode.Exclude,
                IncludeLogs = IncludeLogs,
                IncludeChatHistory = IncludeChatHistory
            };
        }

        Directory.CreateDirectory(folder);
        var fileName = BuildFileName();
        var zipPath = Path.Combine(folder, fileName);

        IsBusy = true;
        StatusText = "Sicherung läuft…";

        try
        {
            var result = await Task.Run(() => _service.CreateAsync(_instanceDir, zipPath, options));

            StatusText = $"Fertig: {Path.GetFileName(result.ZipPath)} ({FormatSize(result.SizeBytes)})";
            _logger?.LogInformation("Sicherung erstellt: {Path} ({Size} Bytes)", result.ZipPath, result.SizeBytes);

            Passphrase = "";
            PassphraseRepeat = "";

            ApplyRotation(folder);
            RefreshBackupList();
        }
        catch (Exception ex)
        {
            StatusText = $"Fehler: {ex.Message}";
            _logger?.LogError(ex, "Sicherung fehlgeschlagen");
        }
        finally
        {
            IsBusy = false;
        }
    }

    [RelayCommand]
    private void ShowSelectedInFolder()
    {
        if (SelectedBackup is null) return;
        SystemShell.RevealFile(SelectedBackup.FullPath);
    }

    [RelayCommand]
    private void DeleteSelected()
    {
        if (SelectedBackup is null) return;

        try
        {
            File.Delete(SelectedBackup.FullPath);
            RefreshBackupList();
            StatusText = $"Gelöscht: {SelectedBackup.FileName}";
        }
        catch (Exception ex)
        {
            StatusText = $"Konnte nicht gelöscht werden: {ex.Message}";
        }
    }

    [RelayCommand]
    private async Task RestoreSelectedAsync()
    {
        if (SelectedBackup is null || RequestRestoreDialog is null) return;

        BackupManifest manifest;
        try
        {
            manifest = await _service.InspectAsync(SelectedBackup.FullPath);
        }
        catch (Exception ex)
        {
            StatusText = $"Sicherung unlesbar: {ex.Message}";
            return;
        }

        var suggestedTarget = SuggestRestoreTarget(manifest);
        var vm = new RestoreBackupViewModel(manifest, suggestedTarget);

        var resultData = await RequestRestoreDialog(vm);
        if (resultData is null) return;

        IsBusy = true;
        StatusText = "Wiederherstellung läuft…";

        try
        {
            var restoreOptions = new RestoreOptions
            {
                Passphrase = resultData.Passphrase,
                Overwrite = resultData.Overwrite
            };

            var result = await Task.Run(() => _service.RestoreAsync(
                SelectedBackup.FullPath, resultData.TargetDirectory, restoreOptions));

            StatusText = $"{result.Written.Count} Datei(en) wiederhergestellt nach {resultData.TargetDirectory}";
            _logger?.LogInformation("Wiederherstellung: {Written} geschrieben nach {Target}",
                result.Written.Count, resultData.TargetDirectory);
        }
        catch (Exception ex)
        {
            StatusText = $"Wiederherstellung fehlgeschlagen: {ex.Message}";
            _logger?.LogError(ex, "Wiederherstellung fehlgeschlagen");
        }
        finally
        {
            IsBusy = false;
        }
    }

    private string BuildFileName()
    {
        var name = string.IsNullOrWhiteSpace(_instanceName) ? "Instanz" : _instanceName;
        var safeName = string.Concat(name.Select(c => Path.GetInvalidFileNameChars().Contains(c) ? '_' : c));
        return $"backup_{safeName}_{DateTime.Now:yyyy-MM-dd_HHmm}.zip";
    }

    private string SuggestRestoreTarget(BackupManifest manifest)
    {
        var parent = Directory.GetParent(_instanceDir)?.FullName ?? _instanceDir;
        var name = string.IsNullOrWhiteSpace(manifest.InstanceName) ? "Instanz" : manifest.InstanceName;
        var safeName = string.Concat(name.Select(c => Path.GetInvalidFileNameChars().Contains(c) ? '_' : c));
        return Path.Combine(parent, $"Instance-{safeName}_wiederhergestellt_{DateTime.Now:yyyyMMdd_HHmm}");
    }

    private void ApplyRotation(string folder)
    {
        if (KeepCount <= 0 || !Directory.Exists(folder)) return;

        try
        {
            var prefix = $"backup_{_instanceName}_";
            var obsolete = new DirectoryInfo(folder)
                .GetFiles("*.zip")
                .Where(f => f.Name.StartsWith(prefix, StringComparison.OrdinalIgnoreCase))
                .OrderByDescending(f => f.LastWriteTime)
                .Skip(KeepCount)
                .ToList();

            foreach (var file in obsolete)
            {
                file.Delete();
                _logger?.LogInformation("Alte Sicherung rotiert: {Name}", file.Name);
            }
        }
        catch (Exception ex)
        {
            _logger?.LogWarning(ex, "Rotation der Sicherungen fehlgeschlagen");
        }
    }

    private static string FormatSize(long bytes) => bytes switch
    {
        < 1024 => $"{bytes} B",
        < 1024 * 1024 => $"{bytes / 1024.0:F1} KB",
        < 1024L * 1024 * 1024 => $"{bytes / 1024.0 / 1024:F1} MB",
        _ => $"{bytes / 1024.0 / 1024 / 1024:F2} GB"
    };
}
