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

namespace SharepointToolbox.ViewModels.Tabs;

/// <summary>
/// ViewModel for the Permissions tab.
/// Orchestrates permission scanning across one or multiple SharePoint sites
/// and exports results to CSV or HTML.
/// </summary>
public partial class PermissionsViewModel : FeatureViewModelBase
{
    private readonly IPermissionsService _permissionsService;
    private readonly ISiteListService _siteListService;
    private readonly ISessionManager _sessionManager;
    private readonly CsvExportService? _csvExportService;
    private readonly HtmlExportService? _htmlExportService;
    private readonly ILogger<FeatureViewModelBase> _logger;

    // ── Observable properties ───────────────────────────────────────────────

    [ObservableProperty]
    private string _siteUrl = string.Empty;

    [ObservableProperty]
    private bool _includeInherited;

    [ObservableProperty]
    private bool _scanFolders = true;

    [ObservableProperty]
    private bool _includeSubsites;

    [ObservableProperty]
    private int _folderDepth = 1;

    /// <summary>
    /// When true, sets FolderDepth to 999 (scan all levels).
    /// </summary>
    public bool IsMaxDepth
    {
        get => FolderDepth >= 999;
        set
        {
            if (value)
                FolderDepth = 999;
            else if (FolderDepth >= 999)
                FolderDepth = 1;
            OnPropertyChanged();
        }
    }

    [ObservableProperty]
    private ObservableCollection<PermissionEntry> _results = new();

    partial void OnFolderDepthChanged(int value) => OnPropertyChanged(nameof(IsMaxDepth));

    // ── Commands ────────────────────────────────────────────────────────────

    public IAsyncRelayCommand ExportCsvCommand { get; }
    public IAsyncRelayCommand ExportHtmlCommand { get; }
    public RelayCommand OpenSitePickerCommand { get; }

    // ── Multi-site ──────────────────────────────────────────────────────────

    public ObservableCollection<SiteInfo> SelectedSites { get; } = new();

    // ── Dialog factory (set by View layer — keeps Window out of ViewModel) ──

    /// <summary>
    /// Factory function set by the View layer to open the SitePickerDialog.
    /// Returns the opened Window; ViewModel calls ShowDialog() on it.
    /// </summary>
    public Func<Window>? OpenSitePickerDialog { get; set; }

    // ── Current tenant profile (received via WeakReferenceMessenger) ────────

    internal TenantProfile? _currentProfile;

    /// <summary>
    /// Public accessor for the current tenant profile — used by View layer dialog factory.
    /// </summary>
    public TenantProfile? CurrentProfile => _currentProfile;

    /// <summary>
    /// Label shown in the UI: "3 site(s) selected" or empty when none are selected.
    /// </summary>
    public string SitesSelectedLabel =>
        SelectedSites.Count > 0
            ? string.Format(Localization.TranslationSource.Instance["perm.sites.selected"], SelectedSites.Count)
            : string.Empty;

    // ── Constructors ────────────────────────────────────────────────────────

    /// <summary>
    /// Full constructor — used by DI and production code.
    /// </summary>
    public PermissionsViewModel(
        IPermissionsService permissionsService,
        ISiteListService siteListService,
        ISessionManager sessionManager,
        CsvExportService csvExportService,
        HtmlExportService htmlExportService,
        ILogger<FeatureViewModelBase> logger)
        : base(logger)
    {
        _permissionsService = permissionsService;
        _siteListService = siteListService;
        _sessionManager = sessionManager;
        _csvExportService = csvExportService;
        _htmlExportService = htmlExportService;
        _logger = logger;

        ExportCsvCommand = new AsyncRelayCommand(ExportCsvAsync, CanExport);
        ExportHtmlCommand = new AsyncRelayCommand(ExportHtmlAsync, CanExport);
        OpenSitePickerCommand = new RelayCommand(ExecuteOpenSitePicker);
        SelectedSites.CollectionChanged += (_, _) => OnPropertyChanged(nameof(SitesSelectedLabel));
    }

    /// <summary>
    /// Test constructor — omits export services (not needed for unit tests).
    /// </summary>
    internal PermissionsViewModel(
        IPermissionsService permissionsService,
        ISiteListService siteListService,
        ISessionManager sessionManager,
        ILogger<FeatureViewModelBase> logger)
        : base(logger)
    {
        _permissionsService = permissionsService;
        _siteListService = siteListService;
        _sessionManager = sessionManager;
        _csvExportService = null;
        _htmlExportService = null;
        _logger = logger;

        ExportCsvCommand = new AsyncRelayCommand(ExportCsvAsync, CanExport);
        ExportHtmlCommand = new AsyncRelayCommand(ExportHtmlAsync, CanExport);
        OpenSitePickerCommand = new RelayCommand(ExecuteOpenSitePicker);
        SelectedSites.CollectionChanged += (_, _) => OnPropertyChanged(nameof(SitesSelectedLabel));
    }

    // ── FeatureViewModelBase implementation ─────────────────────────────────

    protected override async Task RunOperationAsync(CancellationToken ct, IProgress<OperationProgress> progress)
    {
        var urls = SelectedSites.Count > 0
            ? SelectedSites.Select(s => s.Url).ToList()
            : new List<string> { SiteUrl };

        var nonEmpty = urls.Where(u => !string.IsNullOrWhiteSpace(u)).ToList();
        if (nonEmpty.Count == 0)
        {
            StatusMessage = "Enter a site URL or select sites.";
            return;
        }

        var allEntries = new List<PermissionEntry>();
        var scanOptions = new ScanOptions(
            IncludeInherited: IncludeInherited,
            ScanFolders: ScanFolders,
            FolderDepth: FolderDepth,
            IncludeSubsites: IncludeSubsites);

        int i = 0;
        foreach (var url in nonEmpty)
        {
            ct.ThrowIfCancellationRequested();
            progress.Report(new OperationProgress(i, nonEmpty.Count, $"Scanning {url}..."));

            var profile = new TenantProfile
            {
                TenantUrl = url,
                ClientId = _currentProfile?.ClientId ?? string.Empty,
                Name = _currentProfile?.Name ?? string.Empty
            };

            var ctx = await _sessionManager.GetOrCreateContextAsync(profile, ct);
            var siteEntries = await _permissionsService.ScanSiteAsync(ctx, scanOptions, progress, ct);
            allEntries.AddRange(siteEntries);
            i++;
        }

        // Update Results on the UI thread (no-op if no Dispatcher in tests)
        var dispatcher = Application.Current?.Dispatcher;
        if (dispatcher != null)
        {
            await dispatcher.InvokeAsync(() =>
            {
                Results = new ObservableCollection<PermissionEntry>(allEntries);
            });
        }
        else
        {
            Results = new ObservableCollection<PermissionEntry>(allEntries);
        }

        ExportCsvCommand.NotifyCanExecuteChanged();
        ExportHtmlCommand.NotifyCanExecuteChanged();
    }

    // ── Tenant switching ─────────────────────────────────────────────────────

    protected override void OnTenantSwitched(TenantProfile profile)
    {
        _currentProfile = profile;
        Results = new ObservableCollection<PermissionEntry>();
        SiteUrl = string.Empty;
        SelectedSites.Clear();
        OnPropertyChanged(nameof(SitesSelectedLabel));
        OnPropertyChanged(nameof(CurrentProfile));
        ExportCsvCommand.NotifyCanExecuteChanged();
        ExportHtmlCommand.NotifyCanExecuteChanged();
    }

    // ── Internal helpers ─────────────────────────────────────────────────────

    /// <summary>Sets the current tenant profile (for test injection).</summary>
    internal void SetCurrentProfile(TenantProfile profile) => _currentProfile = profile;

    /// <summary>Exposes RunOperationAsync for unit tests (internal + InternalsVisibleTo).</summary>
    internal Task TestRunOperationAsync(CancellationToken ct, IProgress<OperationProgress> progress)
        => RunOperationAsync(ct, progress);

    // ── Command implementations ───────────────────────────────────────────────

    private bool CanExport() => Results.Count > 0;

    private async Task ExportCsvAsync()
    {
        if (_csvExportService == null || Results.Count == 0) return;
        var dialog = new SaveFileDialog
        {
            Title = "Export permissions to CSV",
            Filter = "CSV files (*.csv)|*.csv|All files (*.*)|*.*",
            DefaultExt = "csv",
            FileName = "permissions"
        };
        if (dialog.ShowDialog() != true) return;
        try
        {
            await _csvExportService.WriteAsync(Results, dialog.FileName, CancellationToken.None);
            OpenFile(dialog.FileName);
        }
        catch (Exception ex)
        {
            StatusMessage = $"Export failed: {ex.Message}";
            _logger.LogError(ex, "CSV export failed.");
        }
    }

    private async Task ExportHtmlAsync()
    {
        if (_htmlExportService == null || Results.Count == 0) return;
        var dialog = new SaveFileDialog
        {
            Title = "Export permissions to HTML",
            Filter = "HTML files (*.html)|*.html|All files (*.*)|*.*",
            DefaultExt = "html",
            FileName = "permissions"
        };
        if (dialog.ShowDialog() != true) return;
        try
        {
            await _htmlExportService.WriteAsync(Results, dialog.FileName, CancellationToken.None);
            OpenFile(dialog.FileName);
        }
        catch (Exception ex)
        {
            StatusMessage = $"Export failed: {ex.Message}";
            _logger.LogError(ex, "HTML export failed.");
        }
    }

    private void ExecuteOpenSitePicker()
    {
        if (OpenSitePickerDialog == null) return;
        var dialog = OpenSitePickerDialog.Invoke();
        if (dialog?.ShowDialog() == true && dialog is Views.Dialogs.SitePickerDialog picker)
        {
            SelectedSites.Clear();
            foreach (var site in picker.SelectedUrls)
                SelectedSites.Add(site);
        }
    }

    private static void OpenFile(string filePath)
    {
        try
        {
            Process.Start(new ProcessStartInfo(filePath) { UseShellExecute = true });
        }
        catch
        {
            // Non-critical: file was written successfully, just can't auto-open
        }
    }
}
