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

namespace SharepointToolbox.ViewModels.Tabs;

public partial class TransferViewModel : FeatureViewModelBase
{
    private readonly IFileTransferService _transferService;
    private readonly ISessionManager _sessionManager;
    private readonly BulkResultCsvExportService _exportService;
    private readonly IOwnershipElevationService? _ownershipService;
    private readonly SettingsService? _settingsService;
    private readonly ILogger<FeatureViewModelBase> _logger;
    private TenantProfile? _currentProfile;
    private bool _hasLocalSourceSiteOverride;

    // Source selection
    [ObservableProperty] private string _sourceSiteUrl = string.Empty;
    [ObservableProperty] private string _sourceLibrary = string.Empty;
    [ObservableProperty] private string _sourceFolderPath = string.Empty;

    // Destination selection
    [ObservableProperty] private string _destSiteUrl = string.Empty;
    [ObservableProperty] private string _destLibrary = string.Empty;
    [ObservableProperty] private string _destFolderPath = string.Empty;

    // Transfer options
    [ObservableProperty] private TransferMode _transferMode = TransferMode.Copy;
    [ObservableProperty] private ConflictPolicy _conflictPolicy = ConflictPolicy.Skip;
    [ObservableProperty] private bool _includeSourceFolder;
    [ObservableProperty] private bool _copyFolderContents = true;

    /// <summary>
    /// Library-relative file paths the user checked in the source picker.
    /// When non-empty, only these files are transferred — folder recursion is skipped.
    /// </summary>
    public List<string> SelectedFilePaths { get; } = new();

    /// <summary>Count of per-file selections, for display in the view.</summary>
    public int SelectedFileCount => SelectedFilePaths.Count;

    // Results
    [ObservableProperty] private string _resultSummary = string.Empty;
    [ObservableProperty] private bool _hasFailures;

    private BulkOperationSummary<string>? _lastResult;

    public IAsyncRelayCommand ExportFailedCommand { get; }

    // Expose current profile for View code-behind (site picker, folder browser)
    public TenantProfile? CurrentProfile => _currentProfile;

    // Dialog factories — set by View code-behind
    public Func<string, bool>? ShowConfirmDialog { get; set; }

    public TransferViewModel(
        IFileTransferService transferService,
        ISessionManager sessionManager,
        BulkResultCsvExportService exportService,
        ILogger<FeatureViewModelBase> logger,
        IOwnershipElevationService? ownershipService = null,
        SettingsService? settingsService = null)
        : base(logger)
    {
        _transferService = transferService;
        _sessionManager = sessionManager;
        _exportService = exportService;
        _ownershipService = ownershipService;
        _settingsService = settingsService;
        _logger = logger;

        ExportFailedCommand = new AsyncRelayCommand(ExportFailedAsync, () => HasFailures);
    }

    protected override void OnGlobalSitesChanged(IReadOnlyList<SiteInfo> sites)
    {
        if (_hasLocalSourceSiteOverride) return;
        SourceSiteUrl = sites.Count > 0 ? sites[0].Url : string.Empty;
    }

    partial void OnSourceSiteUrlChanged(string value)
    {
        if (string.IsNullOrWhiteSpace(value))
        {
            _hasLocalSourceSiteOverride = false;
            if (GlobalSites.Count > 0)
                SourceSiteUrl = GlobalSites[0].Url;
        }
        else if (GlobalSites.Count == 0 || value != GlobalSites[0].Url)
        {
            _hasLocalSourceSiteOverride = true;
        }
    }

    protected override async Task RunOperationAsync(CancellationToken ct, IProgress<OperationProgress> progress)
    {
        var T = TranslationSource.Instance;
        if (_currentProfile == null)
            throw new InvalidOperationException(T["err.no_tenant"]);

        if (string.IsNullOrWhiteSpace(SourceSiteUrl) || string.IsNullOrWhiteSpace(SourceLibrary))
            throw new InvalidOperationException(T["err.transfer_source_required"]);

        if (string.IsNullOrWhiteSpace(DestSiteUrl) || string.IsNullOrWhiteSpace(DestLibrary))
            throw new InvalidOperationException(T["err.transfer_dest_required"]);

        // Confirmation dialog
        var message = $"{TransferMode} files from {SourceLibrary} to {DestLibrary} ({ConflictPolicy} on conflict)";
        if (ShowConfirmDialog != null && !ShowConfirmDialog(message))
            return;

        var job = new TransferJob
        {
            SourceSiteUrl = SourceSiteUrl,
            SourceLibrary = SourceLibrary,
            SourceFolderPath = SourceFolderPath,
            DestinationSiteUrl = DestSiteUrl,
            DestinationLibrary = DestLibrary,
            DestinationFolderPath = DestFolderPath,
            Mode = TransferMode,
            ConflictPolicy = ConflictPolicy,
            SelectedFilePaths = SelectedFilePaths.ToList(),
            IncludeSourceFolder = IncludeSourceFolder,
            CopyFolderContents = CopyFolderContents,
        };

        // Build per-site profiles so SessionManager can resolve contexts
        var srcProfile = new TenantProfile
        {
            Name = _currentProfile.Name,
            TenantUrl = SourceSiteUrl,
            ClientId = _currentProfile.ClientId,
        };
        var dstProfile = new TenantProfile
        {
            Name = _currentProfile.Name,
            TenantUrl = DestSiteUrl,
            ClientId = _currentProfile.ClientId,
        };

        var srcCtx = await _sessionManager.GetOrCreateContextAsync(srcProfile, ct);
        var dstCtx = await _sessionManager.GetOrCreateContextAsync(dstProfile, ct);

        var autoOwnership = await IsAutoTakeOwnershipEnabled();

        try
        {
            _lastResult = await _transferService.TransferAsync(srcCtx, dstCtx, job, progress, ct);
        }
        catch (Microsoft.SharePoint.Client.ServerUnauthorizedAccessException ex)
            when (_ownershipService != null && autoOwnership)
        {
            _logger.LogWarning(ex, "Transfer hit access denied — auto-elevating on source and destination.");
            var adminUrl = DeriveAdminUrl(_currentProfile.TenantUrl ?? SourceSiteUrl);
            var adminProfile = new TenantProfile
            {
                Name = _currentProfile.Name,
                TenantUrl = adminUrl,
                ClientId = _currentProfile.ClientId,
            };
            var adminCtx = await _sessionManager.GetOrCreateContextAsync(adminProfile, ct);

            await _ownershipService.ElevateAsync(adminCtx, SourceSiteUrl, string.Empty, ct);
            await _ownershipService.ElevateAsync(adminCtx, DestSiteUrl, string.Empty, ct);

            // Retry with fresh contexts so the new admin membership is honoured.
            srcCtx = await _sessionManager.GetOrCreateContextAsync(srcProfile, ct);
            dstCtx = await _sessionManager.GetOrCreateContextAsync(dstProfile, ct);
            _lastResult = await _transferService.TransferAsync(srcCtx, dstCtx, job, progress, ct);
        }

        // Update UI on dispatcher
        await Application.Current.Dispatcher.InvokeAsync(() =>
        {
            HasFailures = _lastResult.HasFailures;
            ExportFailedCommand.NotifyCanExecuteChanged();

            if (_lastResult.HasFailures)
            {
                ResultSummary = string.Format(
                    Localization.TranslationSource.Instance["bulk.result.success"],
                    _lastResult.SuccessCount, _lastResult.FailedCount);
            }
            else
            {
                ResultSummary = string.Format(
                    Localization.TranslationSource.Instance["bulk.result.allsuccess"],
                    _lastResult.TotalCount);
            }
        });
    }

    private async Task ExportFailedAsync()
    {
        if (_lastResult == null || !_lastResult.HasFailures) return;

        var dlg = new SaveFileDialog
        {
            Filter = "CSV Files (*.csv)|*.csv",
            FileName = "transfer_failed_items.csv",
        };

        if (dlg.ShowDialog() == true)
        {
            await _exportService.WriteFailedItemsCsvAsync(
                _lastResult.FailedItems.ToList(),
                dlg.FileName,
                CancellationToken.None);
            Log.Information("Exported failed transfer items to {Path}", dlg.FileName);
        }
    }

    protected override void OnTenantSwitched(TenantProfile profile)
    {
        _currentProfile = profile;
        _hasLocalSourceSiteOverride = false;
        SourceSiteUrl = string.Empty;
        SourceLibrary = string.Empty;
        SourceFolderPath = string.Empty;
        DestSiteUrl = string.Empty;
        DestLibrary = string.Empty;
        DestFolderPath = string.Empty;
        ResultSummary = string.Empty;
        HasFailures = false;
        SelectedFilePaths.Clear();
        OnPropertyChanged(nameof(SelectedFileCount));
        _lastResult = null;
    }

    /// <summary>Replaces the current per-file selection and notifies the view.</summary>
    public void SetSelectedFiles(IEnumerable<string> libraryRelativePaths)
    {
        SelectedFilePaths.Clear();
        SelectedFilePaths.AddRange(libraryRelativePaths);
        OnPropertyChanged(nameof(SelectedFileCount));
    }

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

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