using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Windows;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using Microsoft.Extensions.Logging;
using Microsoft.Win32;
using SharepointToolbox.Core.Models;
using SharepointToolbox.Localization;
using SharepointToolbox.Services;
using SharepointToolbox.Services.Export;

namespace SharepointToolbox.ViewModels.Tabs;

public partial class VersionCleanupViewModel : FeatureViewModelBase
{
    private readonly IVersionCleanupService _versionService;
    private readonly ISessionManager _sessionManager;
    private readonly VersionCleanupHtmlExportService _htmlExportService;
    private readonly IBrandingService _brandingService;
    private readonly ILogger<FeatureViewModelBase> _logger;
    private TenantProfile? _currentProfile;

    [ObservableProperty]
    private int _keepLast = 5;

    [ObservableProperty]
    private bool _keepFirstVersion;

    [ObservableProperty]
    private bool _confirmDelete = true;

    [ObservableProperty]
    private string _selectedLibrariesLabel = string.Empty;

    public ObservableCollection<string> SelectedLibraries { get; } = new();

    public ObservableCollection<VersionCleanupResult> Results { get; } = new();

    public long TotalBytesFreed => Results.Sum(r => r.BytesFreed);
    public int TotalVersionsDeleted => Results.Sum(r => r.VersionsDeleted);
    public int TotalFilesAffected => Results.Count(r => r.VersionsDeleted > 0);
    public bool HasResults => Results.Count > 0;

    public TenantProfile? CurrentProfile => _currentProfile;

    /// <summary>Set by the view to invoke <see cref="LibraryPickerDialog"/> against the current site.</summary>
    public Func<string, IReadOnlyCollection<string>, Task<IReadOnlyList<string>?>>? PickLibrariesAsync { get; set; }

    /// <summary>Set by the view to display a confirm dialog before destructive run.</summary>
    public Func<string, bool>? ConfirmAction { get; set; }

    public IAsyncRelayCommand SelectLibrariesCommand { get; }
    public IRelayCommand ClearLibrariesCommand { get; }
    public IAsyncRelayCommand ExportCsvCommand { get; }
    public IAsyncRelayCommand ExportHtmlCommand { get; }

    public VersionCleanupViewModel(
        IVersionCleanupService versionService,
        ISessionManager sessionManager,
        VersionCleanupHtmlExportService htmlExportService,
        IBrandingService brandingService,
        ILogger<FeatureViewModelBase> logger)
        : base(logger)
    {
        _versionService = versionService;
        _sessionManager = sessionManager;
        _htmlExportService = htmlExportService;
        _brandingService = brandingService;
        _logger = logger;

        SelectLibrariesCommand = new AsyncRelayCommand(SelectLibrariesAsync, CanPickLibraries);
        ClearLibrariesCommand = new RelayCommand(ClearLibraries);
        ExportCsvCommand = new AsyncRelayCommand(ExportCsvAsync, () => Results.Count > 0);
        ExportHtmlCommand = new AsyncRelayCommand(ExportHtmlAsync, () => Results.Count > 0);

        SelectedLibraries.CollectionChanged += (_, _) => UpdateSelectedLibrariesLabel();
        Results.CollectionChanged += (_, _) =>
        {
            OnPropertyChanged(nameof(HasResults));
            OnPropertyChanged(nameof(TotalBytesFreed));
            OnPropertyChanged(nameof(TotalVersionsDeleted));
            OnPropertyChanged(nameof(TotalFilesAffected));
            ExportCsvCommand.NotifyCanExecuteChanged();
            ExportHtmlCommand.NotifyCanExecuteChanged();
        };
        UpdateSelectedLibrariesLabel();
    }

    protected override void OnTenantSwitched(TenantProfile profile)
    {
        _currentProfile = profile;
        SelectedLibraries.Clear();
        Results.Clear();
        OnPropertyChanged(nameof(CurrentProfile));
        SelectLibrariesCommand.NotifyCanExecuteChanged();
    }

    protected override void OnGlobalSitesChanged(IReadOnlyList<SiteInfo> sites)
    {
        // Site changes invalidate library list — clear so user re-picks.
        SelectedLibraries.Clear();
        SelectLibrariesCommand.NotifyCanExecuteChanged();
    }

    private bool CanPickLibraries() => _currentProfile != null && GlobalSites.Count > 0;

    private async Task SelectLibrariesAsync()
    {
        if (PickLibrariesAsync == null || _currentProfile == null) return;
        var first = GlobalSites.FirstOrDefault();
        if (first == null)
        {
            StatusMessage = TranslationSource.Instance["err.no_sites_selected"];
            return;
        }

        var picked = await PickLibrariesAsync(first.Url, SelectedLibraries.ToArray());
        if (picked == null) return;
        SelectedLibraries.Clear();
        foreach (var t in picked) SelectedLibraries.Add(t);
    }

    private void ClearLibraries() => SelectedLibraries.Clear();

    private void UpdateSelectedLibrariesLabel()
    {
        SelectedLibrariesLabel = SelectedLibraries.Count == 0
            ? TranslationSource.Instance["versions.libs.all"]
            : string.Format(TranslationSource.Instance["versions.libs.count"], SelectedLibraries.Count);
    }

    protected override async Task RunOperationAsync(CancellationToken ct, IProgress<OperationProgress> progress)
    {
        if (_currentProfile == null)
        {
            StatusMessage = TranslationSource.Instance["err.no_tenant_connected"];
            return;
        }

        var urls = GlobalSites.Select(s => s.Url).Where(u => !string.IsNullOrWhiteSpace(u)).ToList();
        if (urls.Count == 0)
        {
            StatusMessage = TranslationSource.Instance["err.no_sites_selected"];
            return;
        }

        if (KeepLast < 0)
        {
            StatusMessage = TranslationSource.Instance["versions.err.keepLast"];
            return;
        }

        if (ConfirmDelete && ConfirmAction != null)
        {
            var msg = string.Format(
                TranslationSource.Instance["versions.confirm"],
                KeepLast,
                KeepFirstVersion ? TranslationSource.Instance["versions.confirm.keepFirst"] : string.Empty);
            if (!ConfirmAction(msg)) return;
        }

        var options = new VersionCleanupOptions(
            SelectedLibraries.ToList(), KeepLast, KeepFirstVersion);

        Results.Clear();
        int siteIdx = 0;
        foreach (var url in urls)
        {
            ct.ThrowIfCancellationRequested();
            siteIdx++;
            progress.Report(new OperationProgress(siteIdx, urls.Count, $"Cleaning {url}..."));

            var siteProfile = new TenantProfile
            {
                TenantUrl = url.TrimEnd('/'),
                ClientId = _currentProfile.ClientId,
                Name = _currentProfile.Name,
            };

            var ctx = await _sessionManager.GetOrCreateContextAsync(siteProfile, ct);
            var siteResults = await _versionService.DeleteOldVersionsAsync(ctx, options, progress, ct);

            if (Application.Current?.Dispatcher is { } dispatcher)
            {
                await dispatcher.InvokeAsync(() =>
                {
                    foreach (var r in siteResults) Results.Add(r);
                });
            }
            else
            {
                foreach (var r in siteResults) Results.Add(r);
            }
        }
    }

    private async Task ExportCsvAsync()
    {
        if (Results.Count == 0) return;
        var dialog = new SaveFileDialog
        {
            Title = "Export version cleanup results",
            Filter = "CSV files (*.csv)|*.csv",
            DefaultExt = "csv",
            FileName = "version_cleanup",
        };
        if (dialog.ShowDialog() != true) return;
        try
        {
            using var w = new System.IO.StreamWriter(dialog.FileName);
            await w.WriteLineAsync("Site,Library,File,Versions Before,Versions Deleted,Versions Remaining,Bytes Freed,Error");
            foreach (var r in Results)
            {
                await w.WriteLineAsync(string.Join(",",
                    Csv(r.SiteUrl),
                    Csv(r.Library),
                    Csv(r.FileServerRelativeUrl),
                    r.VersionsBefore,
                    r.VersionsDeleted,
                    r.VersionsRemaining,
                    r.BytesFreed,
                    Csv(r.Error ?? string.Empty)));
            }
            OpenFile(dialog.FileName);
        }
        catch (Exception ex)
        {
            StatusMessage = $"Export failed: {ex.Message}";
            _logger.LogError(ex, "Version cleanup CSV export failed.");
        }
    }

    private async Task ExportHtmlAsync()
    {
        if (Results.Count == 0) return;
        var dialog = new SaveFileDialog
        {
            Title = "Export version cleanup results to HTML",
            Filter = "HTML files (*.html)|*.html|All files (*.*)|*.*",
            DefaultExt = "html",
            FileName = "version_cleanup",
        };
        if (dialog.ShowDialog() != true) return;
        try
        {
            var mspLogo = await _brandingService.GetMspLogoAsync();
            var clientLogo = _currentProfile?.ClientLogo;
            var branding = new ReportBranding(mspLogo, clientLogo);

            await _htmlExportService.WriteAsync(Results, dialog.FileName, CancellationToken.None, branding);
            OpenFile(dialog.FileName);
        }
        catch (Exception ex)
        {
            StatusMessage = $"Export failed: {ex.Message}";
            _logger.LogError(ex, "Version cleanup HTML export failed.");
        }
    }

    private static void OpenFile(string filePath)
    {
        try { Process.Start(new ProcessStartInfo(filePath) { UseShellExecute = true }); }
        catch { }
    }

    private static string Csv(string value)
    {
        if (string.IsNullOrEmpty(value)) return string.Empty;
        if (value.Contains(',') || value.Contains('"') || value.Contains('\n'))
            return "\"" + value.Replace("\"", "\"\"") + "\"";
        return value;
    }

    partial void OnKeepLastChanged(int value)
    {
        if (value < 0) KeepLast = 0;
    }
}
