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.Core.Helpers;
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 IBrandingService? _brandingService;
    private readonly ISharePointGroupResolver? _groupResolver;
    private readonly ILogger<FeatureViewModelBase> _logger;
    private readonly SettingsService? _settingsService;
    private readonly IOwnershipElevationService? _ownershipService;

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

    [ObservableProperty]
    private bool _includeInherited;

    [ObservableProperty]
    private bool _scanFolders = true;

    /// <summary>Placeholder for the Export Options toggle — no-op in PermissionsViewModel (reserved for future use).</summary>
    [ObservableProperty]
    private bool _mergePermissions;

    [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();

    /// <summary>
    /// When true, displays simplified plain-language labels instead of raw SharePoint role names.
    /// Toggling does not re-run the scan.
    /// </summary>
    [ObservableProperty]
    private bool _isSimplifiedMode;

    /// <summary>
    /// When true, shows individual item-level rows (detailed view).
    /// When false, shows only summary rows grouped by risk level (simple view).
    /// Only meaningful when IsSimplifiedMode is true.
    /// </summary>
    [ObservableProperty]
    private bool _isDetailView = true;

    /// <summary>
    /// Simplified wrappers computed from Results. Rebuilt when Results changes.
    /// </summary>
    private IReadOnlyList<SimplifiedPermissionEntry> _simplifiedResults = Array.Empty<SimplifiedPermissionEntry>();
    public IReadOnlyList<SimplifiedPermissionEntry> SimplifiedResults
    {
        get => _simplifiedResults;
        private set => SetProperty(ref _simplifiedResults, value);
    }

    /// <summary>
    /// Summary counts grouped by risk level. Rebuilt when SimplifiedResults changes.
    /// </summary>
    private IReadOnlyList<PermissionSummary> _summaries = Array.Empty<PermissionSummary>();
    public IReadOnlyList<PermissionSummary> Summaries
    {
        get => _summaries;
        private set => SetProperty(ref _summaries, value);
    }

    /// <summary>
    /// The collection the DataGrid actually binds to. Returns:
    /// - Results (raw) when simplified mode is OFF
    /// - SimplifiedResults when simplified mode is ON and detail view is ON
    /// - (View handles summary display separately via Summaries property)
    /// </summary>
    public object ActiveItemsSource => IsSimplifiedMode
        ? (object)SimplifiedResults
        : Results;

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

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

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

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

    internal TenantProfile? _currentProfile;

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

    /// <summary>
    /// Full constructor — used by DI and production code.
    /// </summary>
    public PermissionsViewModel(
        IPermissionsService permissionsService,
        ISiteListService siteListService,
        ISessionManager sessionManager,
        CsvExportService csvExportService,
        HtmlExportService htmlExportService,
        IBrandingService brandingService,
        ILogger<FeatureViewModelBase> logger,
        ISharePointGroupResolver? groupResolver = null,
        SettingsService? settingsService = null,
        IOwnershipElevationService? ownershipService = null)
        : base(logger)
    {
        _permissionsService = permissionsService;
        _siteListService = siteListService;
        _sessionManager = sessionManager;
        _csvExportService = csvExportService;
        _htmlExportService = htmlExportService;
        _brandingService = brandingService;
        _groupResolver = groupResolver;
        _logger = logger;
        _settingsService = settingsService;
        _ownershipService = ownershipService;

        ExportCsvCommand = new AsyncRelayCommand(ExportCsvAsync, CanExport);
        ExportHtmlCommand = new AsyncRelayCommand(ExportHtmlAsync, CanExport);
    }

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

        ExportCsvCommand = new AsyncRelayCommand(ExportCsvAsync, CanExport);
        ExportHtmlCommand = new AsyncRelayCommand(ExportHtmlAsync, CanExport);
    }

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

    partial void OnIsSimplifiedModeChanged(bool value)
    {
        if (value && Results.Count > 0)
            RebuildSimplifiedData();
        OnPropertyChanged(nameof(ActiveItemsSource));
    }

    partial void OnIsDetailViewChanged(bool value)
    {
        OnPropertyChanged(nameof(ActiveItemsSource));
    }

    /// <summary>
    /// Recomputes SimplifiedResults and Summaries from the current Results collection.
    /// Called when Results changes or when simplified mode is toggled on.
    /// </summary>
    private void RebuildSimplifiedData()
    {
        SimplifiedResults = SimplifiedPermissionEntry.WrapAll(Results);
        Summaries = PermissionSummaryBuilder.Build(SimplifiedResults);
    }

    protected override async Task RunOperationAsync(CancellationToken ct, IProgress<OperationProgress> progress)
    {
        var urls = GlobalSites.Select(s => s.Url).Where(u => !string.IsNullOrWhiteSpace(u)).ToList();
        if (urls.Count == 0)
        {
            StatusMessage = "Select at least one site from the toolbar.";
            return;
        }

        var nonEmpty = urls;

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

        // Read toggle once before the loop (avoids async in exception filter)
        var autoOwnership = await IsAutoTakeOwnershipEnabled();

        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
            };

            bool wasElevated = false;
            IReadOnlyList<PermissionEntry> siteEntries;

            try
            {
                var ctx = await _sessionManager.GetOrCreateContextAsync(profile, ct);
                siteEntries = await _permissionsService.ScanSiteAsync(ctx, scanOptions, progress, ct);
            }
            catch (Exception ex) when (IsAccessDenied(ex) && _ownershipService != null && autoOwnership)
            {
                _logger.LogWarning("Access denied on {Url}, auto-elevating ownership...", url);
                var adminUrl = DeriveAdminUrl(_currentProfile?.TenantUrl ?? url);
                var adminProfile = new TenantProfile
                {
                    TenantUrl = adminUrl,
                    ClientId = _currentProfile?.ClientId ?? string.Empty,
                    Name = _currentProfile?.Name ?? string.Empty
                };
                var adminCtx = await _sessionManager.GetOrCreateContextAsync(adminProfile, ct);
                await _ownershipService.ElevateAsync(adminCtx, url, string.Empty, ct);

                // Retry scan with fresh context
                var retryCtx = await _sessionManager.GetOrCreateContextAsync(profile, ct);
                siteEntries = await _permissionsService.ScanSiteAsync(retryCtx, scanOptions, progress, ct);
                wasElevated = true;
            }

            if (wasElevated)
                allEntries.AddRange(siteEntries.Select(e => e with { WasAutoElevated = true }));
            else
                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);
                if (IsSimplifiedMode)
                    RebuildSimplifiedData();
                OnPropertyChanged(nameof(ActiveItemsSource));
            });
        }
        else
        {
            Results = new ObservableCollection<PermissionEntry>(allEntries);
            if (IsSimplifiedMode)
                RebuildSimplifiedData();
            OnPropertyChanged(nameof(ActiveItemsSource));
        }

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

    // ── Auto-ownership helpers ───────────────────────────────────────────────

    /// <summary>
    /// Derives the tenant admin URL from a standard tenant URL.
    /// E.g. https://tenant.sharepoint.com → https://tenant-admin.sharepoint.com
    /// </summary>
    internal static string DeriveAdminUrl(string tenantUrl)
    {
        var uri = new Uri(tenantUrl.TrimEnd('/'));
        var host = uri.Host;
        if (host.Contains("-admin.sharepoint.com", StringComparison.OrdinalIgnoreCase))
            return tenantUrl;
        var adminHost = host.Replace(".sharepoint.com", "-admin.sharepoint.com",
            StringComparison.OrdinalIgnoreCase);
        return $"{uri.Scheme}://{adminHost}";
    }

    private static bool IsAccessDenied(Exception ex)
    {
        if (ex is Microsoft.SharePoint.Client.ServerUnauthorizedAccessException) return true;
        if (ex is System.Net.WebException webEx && webEx.Response is System.Net.HttpWebResponse resp
            && resp.StatusCode == System.Net.HttpStatusCode.Forbidden) return true;
        return false;
    }

    private async Task<bool> IsAutoTakeOwnershipEnabled()
    {
        if (_settingsService == null) return false;
        var settings = await _settingsService.GetSettingsAsync();
        return settings.AutoTakeOwnership;
    }

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

    protected override void OnTenantSwitched(TenantProfile profile)
    {
        _currentProfile = profile;
        Results = new ObservableCollection<PermissionEntry>();
        SimplifiedResults = Array.Empty<SimplifiedPermissionEntry>();
        Summaries = Array.Empty<PermissionSummary>();
        OnPropertyChanged(nameof(ActiveItemsSource));
        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
        {
            if (IsSimplifiedMode && SimplifiedResults.Count > 0)
                await _csvExportService.WriteAsync(SimplifiedResults.ToList(), dialog.FileName, CancellationToken.None);
            else
                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
        {
            ReportBranding? branding = null;
            if (_brandingService is not null)
            {
                var mspLogo = await _brandingService.GetMspLogoAsync();
                var clientLogo = _currentProfile?.ClientLogo;
                branding = new ReportBranding(mspLogo, clientLogo);
            }

            IReadOnlyDictionary<string, IReadOnlyList<ResolvedMember>>? groupMembers = null;
            if (_groupResolver != null && Results.Count > 0)
            {
                var groupNames = Results
                    .Where(r => r.PrincipalType == "SharePointGroup")
                    .SelectMany(r => r.Users.Split(';', StringSplitOptions.RemoveEmptyEntries))
                    .Select(n => n.Trim())
                    .Where(n => n.Length > 0)
                    .Distinct(StringComparer.OrdinalIgnoreCase)
                    .ToList();

                if (groupNames.Count > 0 && _currentProfile != null)
                {
                    try
                    {
                        var ctx = await _sessionManager.GetOrCreateContextAsync(
                            _currentProfile, CancellationToken.None);
                        groupMembers = await _groupResolver.ResolveGroupsAsync(
                            ctx, _currentProfile.ClientId, groupNames, CancellationToken.None);
                    }
                    catch (Exception ex)
                    {
                        _logger.LogWarning(ex, "Group resolution failed — exporting without member expansion.");
                    }
                }
            }

            if (IsSimplifiedMode && SimplifiedResults.Count > 0)
                await _htmlExportService.WriteAsync(SimplifiedResults.ToList(), dialog.FileName, CancellationToken.None, branding, groupMembers);
            else
                await _htmlExportService.WriteAsync(Results, dialog.FileName, CancellationToken.None, branding, groupMembers);
            OpenFile(dialog.FileName);
        }
        catch (Exception ex)
        {
            StatusMessage = $"Export failed: {ex.Message}";
            _logger.LogError(ex, "HTML export failed.");
        }
    }

    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
        }
    }
}
