using System.Windows;
using System.Windows.Controls;
using Microsoft.SharePoint.Client;
using SharepointToolbox.Core.Helpers;

namespace SharepointToolbox.Views.Dialogs;

public partial class FolderBrowserDialog : Window
{
    private readonly ClientContext _ctx;
    private readonly bool _allowFileSelection;
    private readonly bool _allowFolderCreation;

    public string SelectedLibrary { get; private set; } = string.Empty;
    public string SelectedFolderPath { get; private set; } = string.Empty;

    /// <summary>
    /// Library-relative file paths checked by the user. Only populated when
    /// <paramref name="allowFileSelection"/> was true. Empty if the user picked
    /// a folder node instead.
    /// </summary>
    public IReadOnlyList<string> SelectedFilePaths { get; private set; } = Array.Empty<string>();

    private readonly List<CheckBox> _fileCheckboxes = new();
    private readonly List<TreeViewItem> _expandedNodes = new();

    /// <summary>
    /// Dialog for browsing library folders. Set <paramref name="allowFileSelection"/>
    /// to show individual files (with sizes) and allow ticking them for targeted
    /// transfer. Set <paramref name="allowFolderCreation"/> to expose a "New
    /// Folder" button that creates a folder under the selected node.
    /// </summary>
    public FolderBrowserDialog(ClientContext ctx,
        bool allowFileSelection = false,
        bool allowFolderCreation = false)
    {
        InitializeComponent();
        _ctx = ctx;
        _allowFileSelection = allowFileSelection;
        _allowFolderCreation = allowFolderCreation;
        if (allowFolderCreation)
            ActionBar.Visibility = Visibility.Visible;
        Loaded += OnLoaded;
    }

    private async void OnLoaded(object sender, RoutedEventArgs e)
    {
        try
        {
            var web = _ctx.Web;
            var lists = _ctx.LoadQuery(web.Lists
                .Include(l => l.Title, l => l.Hidden, l => l.BaseType,
                         l => l.RootFolder.ServerRelativeUrl)
                .Where(l => !l.Hidden && l.BaseType == BaseType.DocumentLibrary));
            var progress = new Progress<Core.Models.OperationProgress>();
            await ExecuteQueryRetryHelper.ExecuteQueryRetryAsync(_ctx, progress, CancellationToken.None);

            foreach (var list in lists)
            {
                var rootUrl = list.RootFolder.ServerRelativeUrl.TrimEnd('/');
                var libNode = MakeFolderNode(list.Title,
                    new FolderNodeInfo(list.Title, string.Empty, rootUrl));
                FolderTree.Items.Add(libNode);
            }

            StatusText.Text = $"{FolderTree.Items.Count} libraries loaded.";
        }
        catch (Exception ex)
        {
            StatusText.Text = $"Error: {ex.Message}";
        }
    }

    private TreeViewItem MakeFolderNode(string name, FolderNodeInfo info)
    {
        var node = new TreeViewItem
        {
            Header = name,
            Tag = info,
        };
        // Placeholder child so the expand arrow appears.
        node.Items.Add(new TreeViewItem { Header = "Loading..." });
        node.Expanded += FolderNode_Expanded;
        _expandedNodes.Add(node);
        return node;
    }

    private async void FolderNode_Expanded(object sender, RoutedEventArgs e)
    {
        if (sender is not TreeViewItem node || node.Tag is not FolderNodeInfo info)
            return;

        // Only load children once.
        if (!(node.Items.Count == 1
              && node.Items[0] is TreeViewItem dummy
              && dummy.Header?.ToString() == "Loading..."))
            return;

        node.Items.Clear();
        try
        {
            var folder = _ctx.Web.GetFolderByServerRelativeUrl(info.ServerRelativeUrl);
            _ctx.Load(folder, f => f.StorageMetrics.TotalSize,
                              f => f.StorageMetrics.TotalFileCount);
            var list = _ctx.Web.Lists.GetByTitle(info.LibraryTitle);
            _ctx.Load(list, l => l.Title);
            var progress = new Progress<Core.Models.OperationProgress>();
            await ExecuteQueryRetryHelper.ExecuteQueryRetryAsync(_ctx, progress, CancellationToken.None);

            // Annotate the parent node header with total metrics now that they loaded.
            node.Header = FormatFolderHeader(info.LibraryTitle == info.RelativePath || string.IsNullOrEmpty(info.RelativePath)
                    ? (string)node.Header!
                    : System.IO.Path.GetFileName(info.RelativePath),
                folder.StorageMetrics.TotalFileCount,
                folder.StorageMetrics.TotalSize);

            // Enumerate direct children via paginated CAML — Folder.Folders /
            // Folder.Files lazy loading hits the list-view threshold on libraries
            // above 5,000 items even when only a small folder is being expanded.
            var subFolders = new List<(string Name, string Url)>();
            var filesInFolder = new List<(string Name, long Length, string Url)>();

            await foreach (var item in SharePointPaginationHelper.GetItemsInFolderAsync(
                _ctx, list, info.ServerRelativeUrl, recursive: false,
                viewFields: new[] { "FSObjType", "FileLeafRef", "FileRef", "File_x0020_Size" },
                ct: CancellationToken.None))
            {
                var fsType  = item["FSObjType"]?.ToString();
                var name    = item["FileLeafRef"]?.ToString() ?? string.Empty;
                var fileRef = item["FileRef"]?.ToString() ?? string.Empty;

                if (fsType == "1")
                {
                    if (name.StartsWith("_") || name == "Forms") continue;
                    subFolders.Add((name, fileRef));
                }
                else if (fsType == "0" && _allowFileSelection)
                {
                    long.TryParse(item["File_x0020_Size"]?.ToString() ?? "0", out long size);
                    filesInFolder.Add((name, size, fileRef));
                }
            }

            // Batch-load StorageMetrics for each subfolder in one round-trip.
            var metricFolders = subFolders
                .Select(sf => _ctx.Web.GetFolderByServerRelativeUrl(sf.Url))
                .ToList();
            foreach (var mf in metricFolders)
                _ctx.Load(mf, f => f.StorageMetrics.TotalSize,
                              f => f.StorageMetrics.TotalFileCount);
            if (metricFolders.Count > 0)
                await ExecuteQueryRetryHelper.ExecuteQueryRetryAsync(_ctx, progress, CancellationToken.None);

            for (int i = 0; i < subFolders.Count; i++)
            {
                var (name, url) = subFolders[i];
                var childRelative = string.IsNullOrEmpty(info.RelativePath)
                    ? name
                    : $"{info.RelativePath}/{name}";

                var childInfo = new FolderNodeInfo(info.LibraryTitle, childRelative, url);

                var childNode = MakeFolderNode(
                    FormatFolderHeader(name,
                        metricFolders[i].StorageMetrics.TotalFileCount,
                        metricFolders[i].StorageMetrics.TotalSize),
                    childInfo);
                node.Items.Add(childNode);
            }

            // Files under this folder — only shown when selection is enabled.
            if (_allowFileSelection)
            {
                foreach (var (fileName, fileSize, _) in filesInFolder)
                {
                    var fileRel = string.IsNullOrEmpty(info.RelativePath)
                        ? fileName
                        : $"{info.RelativePath}/{fileName}";

                    var cb = new CheckBox
                    {
                        Content = $"{fileName}   ({FormatSize(fileSize)})",
                        Tag = new FileNodeInfo(info.LibraryTitle, fileRel),
                        Margin = new Thickness(4, 2, 0, 2),
                    };
                    cb.Checked += FileCheckbox_Toggled;
                    cb.Unchecked += FileCheckbox_Toggled;
                    _fileCheckboxes.Add(cb);

                    var fileItem = new TreeViewItem { Header = cb, Focusable = false };
                    node.Items.Add(fileItem);
                }
            }
        }
        catch (Exception ex)
        {
            node.Items.Add(new TreeViewItem { Header = $"Error: {ex.Message}" });
        }
    }

    private static string FormatFolderHeader(string name, long fileCount, long totalBytes)
    {
        if (fileCount <= 0) return name;
        return $"{name}   ({fileCount} files, {FormatSize(totalBytes)})";
    }

    private static string FormatSize(long bytes)
    {
        if (bytes <= 0) return "0 B";
        if (bytes >= 1_073_741_824L) return $"{bytes / 1_073_741_824.0:F2} GB";
        if (bytes >= 1_048_576L)     return $"{bytes / 1_048_576.0:F2} MB";
        if (bytes >= 1024L)          return $"{bytes / 1024.0:F2} KB";
        return $"{bytes} B";
    }

    private void FolderTree_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
    {
        if (e.NewValue is TreeViewItem node && node.Tag is FolderNodeInfo info)
        {
            SelectedLibrary = info.LibraryTitle;
            SelectedFolderPath = info.RelativePath;
            SelectButton.IsEnabled = true;
            NewFolderButton.IsEnabled = _allowFolderCreation;
        }
        else
        {
            // File nodes have CheckBox headers, not FolderNodeInfo tags.
            NewFolderButton.IsEnabled = false;
        }
    }

    private void FileCheckbox_Toggled(object sender, RoutedEventArgs e)
    {
        // Enable "Select" as soon as any file is checked — user can confirm
        // purely via file selection without also picking a folder node.
        if (_fileCheckboxes.Any(c => c.IsChecked == true))
            SelectButton.IsEnabled = true;
    }

    private async void NewFolder_Click(object sender, RoutedEventArgs e)
    {
        if (FolderTree.SelectedItem is not TreeViewItem node ||
            node.Tag is not FolderNodeInfo info)
            return;

        var dlg = new InputDialog("New folder name:", string.Empty)
        {
            Owner = this
        };
        if (dlg.ShowDialog() != true) return;
        var folderName = dlg.ResponseText.Trim();
        if (string.IsNullOrEmpty(folderName)) return;

        try
        {
            var parent = _ctx.Web.GetFolderByServerRelativeUrl(info.ServerRelativeUrl);
            var created = parent.Folders.Add(folderName);
            _ctx.Load(created, f => f.ServerRelativeUrl, f => f.Name);
            var progress = new Progress<Core.Models.OperationProgress>();
            await ExecuteQueryRetryHelper.ExecuteQueryRetryAsync(_ctx, progress, CancellationToken.None);

            var childRelative = string.IsNullOrEmpty(info.RelativePath)
                ? created.Name
                : $"{info.RelativePath}/{created.Name}";
            var childInfo = new FolderNodeInfo(
                info.LibraryTitle, childRelative, created.ServerRelativeUrl);
            var childNode = MakeFolderNode(created.Name, childInfo);

            // Expand the parent so the fresh folder is visible immediately.
            node.IsExpanded = true;
            node.Items.Add(childNode);
            StatusText.Text = $"Created: {created.ServerRelativeUrl}";
        }
        catch (Exception ex)
        {
            StatusText.Text = $"Error: {ex.Message}";
        }
    }

    private void Select_Click(object sender, RoutedEventArgs e)
    {
        // Harvest checked files (library-relative paths).
        SelectedFilePaths = _fileCheckboxes
            .Where(c => c.IsChecked == true && c.Tag is FileNodeInfo)
            .Select(c => ((FileNodeInfo)c.Tag!).RelativePath)
            .ToList();

        // If files were picked but no folder node was selected, borrow the
        // library from the first file so the caller still has a valid target.
        if (SelectedFilePaths.Count > 0 && string.IsNullOrEmpty(SelectedLibrary))
        {
            var firstTag = (FileNodeInfo)_fileCheckboxes
                .First(c => c.IsChecked == true && c.Tag is FileNodeInfo).Tag!;
            SelectedLibrary = firstTag.LibraryTitle;
            SelectedFolderPath = string.Empty;
        }

        DialogResult = true;
        Close();
    }

    private void Cancel_Click(object sender, RoutedEventArgs e)
    {
        DialogResult = false;
        Close();
    }

    protected override void OnClosed(EventArgs e)
    {
        Loaded -= OnLoaded;
        foreach (var node in _expandedNodes)
            node.Expanded -= FolderNode_Expanded;
        _expandedNodes.Clear();
        foreach (var cb in _fileCheckboxes)
        {
            cb.Checked -= FileCheckbox_Toggled;
            cb.Unchecked -= FileCheckbox_Toggled;
        }
        _fileCheckboxes.Clear();
        base.OnClosed(e);
    }

    private record FolderNodeInfo(string LibraryTitle, string RelativePath, string ServerRelativeUrl);
    private record FileNodeInfo(string LibraryTitle, string RelativePath);
}
