@using MoonCore.Helpers
@using MoonCore.Services
@using MoonCoreUI.Services
@using Moonlight.Core.Configuration
@using Moonlight.Core.Services
@using Moonlight.Features.FileManager.Interfaces
@using Moonlight.Features.FileManager.Models.Abstractions.FileAccess
@using Moonlight.Features.FileManager.Services

@inject AlertService AlertService
@inject ToastService ToastService
@inject FileManagerInteropService FileManagerInteropService
@inject SharedFileAccessService FileAccessService
@inject ConfigService<CoreConfiguration> ConfigService
@inject PluginService PluginService
@inject IServiceProvider ServiceProvider

@implements IDisposable

<div class="card card-body px-5">
    <div class="d-flex justify-content-center justify-content-md-between">
        <div class="d-none d-md-flex justify-content-start align-items-center">
            <div class="badge badge-primary badge-lg fs-5 py-2 text-center">
                @{
                    var parts = Path
                        .Split("/")
                        .Where(x => !string.IsNullOrEmpty(x))
                        .ToArray();

                    var i = 1;
                }

                <a href="#" class="text-white mx-1" @onclick:preventDefault @onclick="() => NavigateBackToLevel(0)">/</a>

                @foreach (var part in parts)
                {
                    var x = i + 0;

                    <a href="#" class="text-white mx-1" @onclick:preventDefault @onclick="() => NavigateBackToLevel(x)">@(part)</a>
                    <div class="mx-2 text-white">/</div>

                    i++;
                }
            </div>
        </div>
        <div class="d-flex justify-content-center justify-content-md-end align-items-center">
            @if (View != null && View.Selection.Any())
            {
                foreach (var action in SelectionActions)
                {
                    var cssClass = $"btn btn-{action.Color} mx-2";

                    <WButton Text="@action.Name" CssClasses="@cssClass" OnClick="() => InvokeSelectionAction(action)"/>
                }
            }
            else
            {
                <WButton OnClick="ManualRefresh" CssClasses="btn btn-icon btn-light-info">
                    <i class="bx bx-sm bx-refresh"></i>
                </WButton>
                <label for="fileManagerSelect" class="btn btn-light-primary mx-2">Upload</label>
                <input id="fileManagerSelect" type="file" hidden="hidden" multiple/>
                <div class="dropdown">
                    <a class="btn btn-primary dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false">
                        New
                    </a>
                    <ul class="dropdown-menu" aria-labelledby="dropdownMenuLink">
                        @foreach (var action in CreateActions)
                        {
                            <li>
                                <a href="#" class="dropdown-item" @onclick:preventDefault @onclick="() => InvokeCreateAction(action)">
                                    <i class="bx bx-sm @action.Icon text-@action.Color me-2 align-middle"></i>
                                    <span class="align-middle fs-6">@action.Name</span>
                                </a>
                            </li>
                        }
                    </ul>
                </div>
            }
        </div>
    </div>
</div>

@if (ShowEditor)
{
    <div class="card card-body px-2 py-2 mt-5">
        <FileEditor @ref="Editor" FileAccess="FileAccess" File="FileToEdit" OnClosed="CloseEditor"/>
    </div>
}
else
{
    <div id="fileManagerUpload" class="card card-body px-5 py-3 mt-5">
        <FileView @ref="View"
                  FileAccess="FileAccess"
                  OnEntryClicked="OnEntryClicked"
                  OnNavigateUpClicked="OnNavigateUpClicked"
                  OnSelectionChanged="OnSelectionChanged"
                  EnableContextMenu="true">
            <ContextMenuTemplate>
                @foreach (var action in ContextActions)
                {
                    if (!action.Filter.Invoke(context))
                        continue;

                    <a class="dropdown-item" href="#" @onclick:preventDefault @onclick="() => InvokeContextAction(action, context)">
                        <i class="bx bx-sm @action.Icon text-@action.Color align-middle"></i>
                        <span class="align-middle ms-3">@action.Name</span>
                    </a>
                }
            </ContextMenuTemplate>
        </FileView>
    </div>

    <SmartModal @ref="FolderSelectModal" CssClasses="modal-lg modal-dialog-centered">
        <div class="modal-header">
            <h5 class="modal-title">@FolderSelectTitle</h5>
            <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close" @onclick="HideFolderSelect"></button>
        </div>
        <div class="modal-body">
            <FileView @ref="FolderSelectView"
                      FileAccess="FolderSelectFileAccess"
                      Filter="FolderSelectFilter"
                      ShowDate="false"
                      ShowSelect="false"
                      ShowSize="false"
                      OnEntryClicked="EntryClickFolderSelect"
                      OnNavigateUpClicked="NavigateUpFolderSelect"
                      EnableContextMenu="false"/>
        </div>
        <div class="modal-footer">
            <button type="button" class="btn btn-secondary" data-bs-dismiss="modal" @onclick="HideFolderSelect">Cancel</button>
            <button type="button" class="btn btn-primary" @onclick="SubmitFolderSelect">Submit</button>
        </div>
    </SmartModal>
}

@code
{
    [Parameter] public BaseFileAccess FileAccess { get; set; }

    public FileView View { get; private set; }
    private string Path = "/";

    private IFileManagerContextAction[] ContextActions;
    private IFileManagerSelectionAction[] SelectionActions;
    private IFileManagerCreateAction[] CreateActions;

    // Editor
    private FileEditor Editor;
    private FileEntry FileToEdit;
    private bool ShowEditor = false;

    // Folder select dialog
    private bool FolderSelectIsOpen = false;
    private SmartModal FolderSelectModal;
    private BaseFileAccess FolderSelectFileAccess;
    private string FolderSelectTitle;
    private Func<string, Task> FolderSelectResult;
    private FileView FolderSelectView;
    private Func<FileEntry, bool> FolderSelectFilter => entry => entry.IsDirectory;

    private Timer? UploadTokenTimer;

    protected override async Task OnInitializedAsync()
    {
        // Load plugin ui and options
        ContextActions = await PluginService.GetImplementations<IFileManagerContextAction>();
        SelectionActions = await PluginService.GetImplementations<IFileManagerSelectionAction>();
        CreateActions = await PluginService.GetImplementations<IFileManagerCreateAction>();
    }

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (!firstRender)
            return;


        // Setup upload url update timer
        UploadTokenTimer = new(async _ =>
        {
            await FileAccessService.Register(FileAccess);
            var token = await FileAccessService.GenerateToken(FileAccess);
            var url = $"/api/upload?token={token}";

            await FileManagerInteropService.UpdateUrl("fileManager", url);
        }, null, TimeSpan.FromMinutes(5), TimeSpan.FromMinutes(5));

        // Create initial url
        await FileAccessService.Register(FileAccess);
        var token = await FileAccessService.GenerateToken(FileAccess);
        var url = $"/api/upload?token={token}";

        // Refresh the file view when a upload is completed
        FileManagerInteropService.OnUploadStateChanged += async () => { await View.Refresh(); };

        // Initialize drop area & file select
        await FileManagerInteropService.UpdateUrl("fileManager", url);
        await FileManagerInteropService.InitDropzone("fileManagerUpload", "fileManager");
        await FileManagerInteropService.InitFileSelect("fileManagerSelect", "fileManager");
    }

    private async Task OnEntryClicked(FileEntry entry)
    {
        if (entry.IsFile)
        {
            var fileSizeInKilobytes = ByteSizeValue.FromBytes(entry.Size).KiloBytes;

            if (fileSizeInKilobytes > ConfigService.Get().Customisation.FileManager.MaxFileOpenSize)
            {
                await ToastService.Danger("Unable to open file as it exceeds the max file size limit");
                return;
            }

            await OpenEditor(entry);
        }
        else
        {
            await FileAccess.ChangeDirectory(entry.Name);
            await View.Refresh();

            await Refresh();
        }
    }

    private async Task InvokeContextAction(IFileManagerContextAction contextAction, FileEntry entry)
    {
        await View.HideContextMenu();

        await contextAction.Execute(FileAccess, this, entry, ServiceProvider);
    }

    private async Task InvokeSelectionAction(IFileManagerSelectionAction action)
    {
        await action.Execute(FileAccess, this, View.Selection, ServiceProvider);

        // Refresh resets the selection 
        await View.Refresh();
    }

    private async Task InvokeCreateAction(IFileManagerCreateAction action)
    {
        await action.Execute(FileAccess, this, ServiceProvider);
    }

    private async Task OnSelectionChanged(FileEntry[] _) => await InvokeAsync(StateHasChanged);

    #region Navigation & Refreshing

    private async Task OnNavigateUpClicked()
    {
        await FileAccess.ChangeDirectory("..");
        await View.Refresh();

        await Refresh();
    }

    private async Task NavigateBackToLevel(int level)
    {
        if (ShowEditor) // Ignore navigation events while the editor is open
            return;

        var path = await FileAccess.GetCurrentDirectory();

        var parts = path.Split("/");
        var pathToNavigate = string.Join("/", parts.Take(level + 1)) + "/";

        await FileAccess.SetDirectory(pathToNavigate);
        await View.Refresh();
        await Refresh();
    }

    private async Task ManualRefresh()
    {
        if (ShowEditor) // Ignore refresh while editor is open
            return;

        await View.Refresh();
        await Refresh();

        await ToastService.Info("Refreshed");
    }

    private async Task Refresh()
    {
        Path = await FileAccess.GetCurrentDirectory();

        await InvokeAsync(StateHasChanged);
    }

    #endregion

    #region File Editor

    public async Task OpenEditor(FileEntry entry)
    {
        FileToEdit = entry;
        ShowEditor = true;

        await InvokeAsync(StateHasChanged);
    }

    public async Task CloseEditor()
    {
        ShowEditor = false;
        await InvokeAsync(StateHasChanged);
    }

    #endregion

    #region Selects

    public async Task OpenFolderSelect(string title, Func<string, Task> onResult)
    {
        if (FolderSelectIsOpen)
            await HideFolderSelect();

        FolderSelectResult = onResult;
        FolderSelectTitle = title;

        FolderSelectFileAccess = FileAccess.Clone();
        await FolderSelectFileAccess.SetDirectory("/");

        await FolderSelectModal.Show();
    }

    public async Task HideFolderSelect()
    {
        await FolderSelectModal.Hide();
        FolderSelectIsOpen = false;
        FolderSelectFileAccess.Dispose();
    }

    private async Task SubmitFolderSelect()
    {
        var path = await FolderSelectFileAccess.GetCurrentDirectory();

        await HideFolderSelect();

        await FolderSelectResult.Invoke(path);
    }

    private async Task NavigateUpFolderSelect()
    {
        await FolderSelectFileAccess.ChangeDirectory("..");
        await FolderSelectView.Refresh();
    }

    private async Task EntryClickFolderSelect(FileEntry entry)
    {
        await FolderSelectFileAccess.ChangeDirectory(entry.Name);
        await FolderSelectView.Refresh();
    }

    #endregion

    public async void Dispose()
    {
        if (UploadTokenTimer != null)
            await UploadTokenTimer.DisposeAsync();

        await FileAccessService.Unregister(FileAccess);
    }
}