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 SearchViewModel : FeatureViewModelBase
{
    private readonly ISearchService _searchService;
    private readonly ISessionManager _sessionManager;
    private readonly SearchCsvExportService _csvExportService;
    private readonly SearchHtmlExportService _htmlExportService;
    private readonly IBrandingService _brandingService;
    private readonly ILogger<FeatureViewModelBase> _logger;
    private TenantProfile? _currentProfile;

    // ── Filter observable properties ─────────────────────────────────────────

    [ObservableProperty] private string _extensions = string.Empty;
    [ObservableProperty] private string _regex = string.Empty;
    [ObservableProperty] private bool _useCreatedAfter;
    [ObservableProperty] private DateTime _createdAfter = DateTime.Today.AddMonths(-1);
    [ObservableProperty] private bool _useCreatedBefore;
    [ObservableProperty] private DateTime _createdBefore = DateTime.Today;
    [ObservableProperty] private bool _useModifiedAfter;
    [ObservableProperty] private DateTime _modifiedAfter = DateTime.Today.AddMonths(-1);
    [ObservableProperty] private bool _useModifiedBefore;
    [ObservableProperty] private DateTime _modifiedBefore = DateTime.Today;
    [ObservableProperty] private string _createdBy = string.Empty;
    [ObservableProperty] private string _modifiedBy = string.Empty;
    [ObservableProperty] private string _library = string.Empty;
    [ObservableProperty] private int _maxResults = 5000;

    private ObservableCollection<SearchResult> _results = new();
    public ObservableCollection<SearchResult> Results
    {
        get => _results;
        private set
        {
            _results = value;
            OnPropertyChanged();
            ExportCsvCommand.NotifyCanExecuteChanged();
            ExportHtmlCommand.NotifyCanExecuteChanged();
        }
    }

    public IAsyncRelayCommand ExportCsvCommand { get; }
    public IAsyncRelayCommand ExportHtmlCommand { get; }
    public TenantProfile? CurrentProfile => _currentProfile;

    public SearchViewModel(
        ISearchService searchService,
        ISessionManager sessionManager,
        SearchCsvExportService csvExportService,
        SearchHtmlExportService htmlExportService,
        IBrandingService brandingService,
        ILogger<FeatureViewModelBase> logger)
        : base(logger)
    {
        _searchService    = searchService;
        _sessionManager   = sessionManager;
        _csvExportService = csvExportService;
        _htmlExportService = htmlExportService;
        _brandingService  = brandingService;
        _logger           = logger;

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

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

        var allItems = new List<SearchResult>();

        foreach (var url in urls)
        {
            var siteProfile = new TenantProfile
            {
                TenantUrl = url.TrimEnd('/'),
                ClientId  = _currentProfile.ClientId,
                Name      = _currentProfile.Name
            };
            var ctx = await _sessionManager.GetOrCreateContextAsync(siteProfile, ct);

            var opts = new SearchOptions(
                Extensions:    ParseExtensions(Extensions),
                Regex:         string.IsNullOrWhiteSpace(Regex) ? null : Regex,
                CreatedAfter:  UseCreatedAfter  ? CreatedAfter  : null,
                CreatedBefore: UseCreatedBefore ? CreatedBefore : null,
                ModifiedAfter: UseModifiedAfter  ? ModifiedAfter  : null,
                ModifiedBefore: UseModifiedBefore ? ModifiedBefore : null,
                CreatedBy:     string.IsNullOrWhiteSpace(CreatedBy)  ? null : CreatedBy,
                ModifiedBy:    string.IsNullOrWhiteSpace(ModifiedBy) ? null : ModifiedBy,
                Library:       string.IsNullOrWhiteSpace(Library)    ? null : Library,
                MaxResults:    Math.Clamp(MaxResults, 1, 50_000),
                SiteUrl:       url.TrimEnd('/')
            );

            var items = await _searchService.SearchFilesAsync(ctx, opts, progress, ct);
            allItems.AddRange(items);
        }

        if (Application.Current?.Dispatcher is { } dispatcher)
            await dispatcher.InvokeAsync(() => Results = new ObservableCollection<SearchResult>(allItems));
        else
            Results = new ObservableCollection<SearchResult>(allItems);
    }

    protected override void OnTenantSwitched(Core.Models.TenantProfile profile)
    {
        _currentProfile = profile;
        Results = new ObservableCollection<SearchResult>();
        OnPropertyChanged(nameof(CurrentProfile));
        ExportCsvCommand.NotifyCanExecuteChanged();
        ExportHtmlCommand.NotifyCanExecuteChanged();
    }

    internal void SetCurrentProfile(TenantProfile profile) => _currentProfile = profile;

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

    private async Task ExportCsvAsync()
    {
        if (Results.Count == 0) return;
        var dialog = new SaveFileDialog
        {
            Title = "Export search results to CSV",
            Filter = "CSV files (*.csv)|*.csv|All files (*.*)|*.*",
            DefaultExt = "csv",
            FileName = "search_results"
        };
        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 (Results.Count == 0) return;
        var dialog = new SaveFileDialog
        {
            Title = "Export search results to HTML",
            Filter = "HTML files (*.html)|*.html|All files (*.*)|*.*",
            DefaultExt = "html",
            FileName = "search_results"
        };
        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);
            }

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

    private static string[] ParseExtensions(string input)
    {
        if (string.IsNullOrWhiteSpace(input)) return Array.Empty<string>();
        return input.Split(new[] { ' ', ',', ';' }, StringSplitOptions.RemoveEmptyEntries)
                    .Select(e => e.TrimStart('.').ToLowerInvariant())
                    .Where(e => e.Length > 0)
                    .Distinct()
                    .ToArray();
    }

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