@page "/drive"
@page "/drive/{Id}"
@attribute [Authorize]
@using Iceshrimp.Frontend.Localization
@using Microsoft.AspNetCore.Components.Sections
@using Microsoft.Extensions.Localization
@using Iceshrimp.Assets.PhosphorIcons
@using Iceshrimp.Frontend.Core.Miscellaneous
@using Iceshrimp.Frontend.Core.Services
@using Iceshrimp.Shared.Schemas.Web
@using Microsoft.AspNetCore.Authorization
@using Iceshrimp.Frontend.Components
@inject ApiService                     Api;
@inject GlobalComponentSvc             Global;
@inject IJSRuntime                     Js;
@inject IStringLocalizer<Localization> Loc;
@inject ILogger<DrivePage>             Logger;
@inject NavigationManager              Nav;

<HeadTitle Text="@Loc["Drive"]"/>

<SectionContent SectionName="top-bar">
    @if (State == State.Loaded && Folder is { Id: not null, Name: not null })
    {
        <span class="btn" @onclick="NavigateToParent">
            <Icon Name="Icons.ArrowLeft"/>
        </span>
        @Folder.Name
    }
    else
    {
        <Icon Name="Icons.Cloud"/>
        @Loc["Drive"]
    }
    @if (State == State.Loaded && Folder is not null)
    {
        <span class="action btn" @onclick="CreateFolder" title="@Loc["Create folder"]">
            <Icon Name="Icons.FolderPlus"/>
        </span>
        <span class="action btn" @onclick="OpenUpload" title="@Loc["Upload file"]">
            <Icon Name="Icons.Upload"/>
        </span>
        @if (Folder is { Id: not null, Files: [], Folders: [] })
        {
            <span class="action btn" @onclick="DeleteFolder" title="@Loc["Delete folder"]">
                <Icon Name="Icons.FolderMinus"/>
            </span>
        }
    }
</SectionContent>

<div class="file-input">
    <InputFile @ref="UploadInput" OnChange="Upload">Upload!</InputFile>
</div>

@if (State == State.Loaded && Folder != null)
{
    <ol class="drive-files">
        @foreach (var el in Folder.Folders)
        {
            <li>
                <DriveEntry Folder="el" FolderList="@Folder.Folders" ParentId="@Id"/>
            </li>
        }
        @foreach (var el in Folder.Files)
        {
            <li>
                <DriveEntry File="el" FolderList="@Folder.Folders" ParentId="@Id"/>
            </li>
        }
    </ol>
}
@if (State == State.Loading)
{
    <div class="loading"><LoadingSpinner Scale="2"/></div>
}
@if (State == State.NotFound)
{
    <div>This folder does not exist</div>
}
@if (State == State.Error)
{
    <div>An error occured while loading the drive folder. Please inspect logs.</div>
}

@code {
    [Parameter] public string?              Id          { get; set; }
    private            DriveFolderResponse? Folder      { get; set; }
    private            State                State      { get; set; }
    private            InputFile            UploadInput { get; set; } = null!;
    private            IJSObjectReference   _module = null!;

    private async Task Load()
    {
        Logger.LogTrace($"Opening drive folder: {Id ?? "root"}");
        State = State.Loading;

        try
        {
            Folder = await Api.Drive.GetFolderAsync(Id);
        }
        catch (ApiException e)
        {
            Logger.LogWarning($"Failed to load folder '{Id ?? "root"}' due to API Exception: {e.Message}");
            State = State.Error;
            return;
        }

        if (Folder == null)
        {
            State = State.NotFound;
            return;
        }

        State = State.Loaded;
    }

    protected override async Task OnInitializedAsync()
    {
        await Load();
        _module = await Js.InvokeAsync<IJSObjectReference>("import",
                                                           "./Pages/DrivePage.razor.js");
    }
    
    protected override async Task OnParametersSetAsync()
    {
        await Load();
    }

    private void NavigateToParent()
    {
        Nav.NavigateTo(Folder?.ParentId != null ? $"/drive/{Folder.ParentId}" : "/drive");
    }

    private async Task CreateFolder() =>
        await Global.PromptDialog?.Prompt(new EventCallback<string?>(this, CreateFolderCallback), Loc["Create folder"], Loc["Folder name"], null, buttonText: Loc["Create"])!;

    private async Task CreateFolderCallback(string? name)
    {
        if (string.IsNullOrWhiteSpace(name)) return;

        try
        {
            var res = await Api.Drive.CreateFolderAsync(new DriveFolderRequest { Name = name, ParentId = Folder!.Id });
            if (res != null)
            {
                Folder.Folders.Add(res);
                StateHasChanged();
            }
        }
        catch (ApiException e)
        {
            await Global.NoticeDialog?.Display(e.Response.Message ?? Loc["An unknown error occurred"], NoticeDialog.NoticeType.Error)!;
        }
    }

    // The <InputFile> Component is hidden, and triggered by a sepperate button.
    // That way we get it's functionality, without the styling limitations of the InputFile component
    private async Task OpenUpload()
    {
        await _module.InvokeVoidAsync("openUpload", UploadInput.Element);
    }
    
    private async Task Upload(InputFileChangeEventArgs e)
    {
        var res = Folder?.Id != null ? await Api.Drive.UploadFileToFolderAsync(e.File, Folder.Id) : await Api.Drive.UploadFileAsync(e.File);
        Folder!.Files.Insert(0, res);
        StateHasChanged();
    }

    private async Task DeleteFolder()
    {
        if (Folder is not { Id: not null, Files: [], Folders: [] }) return;
        await Global.ConfirmDialog?.Confirm(new EventCallback<bool>(this, DeleteFolderCallback), Loc["Delete {0}?", Folder.Name ?? ""], Icons.Trash, Loc["Delete"])!;
    }

    private async Task DeleteFolderCallback(bool delete)
    {
        if (Folder is not { Id: not null, Files: [], Folders: [] }) return;
        if (!delete) return;

        try
        {
            await Api.Drive.DeleteFolderAsync(Folder.Id);
            NavigateToParent();
        }
        catch (ApiException e)
        {
            await Global.NoticeDialog?.Display(e.Response.Message ?? Loc["An unknown error occurred"], NoticeDialog.NoticeType.Error)!;
        }
    }
}