@namespace OpenArchival.Blazor.CustomComponents

@using Microsoft.Extensions.Logging
@using Microsoft.Extensions.Options
@using OpenArchival.DataAccess
@using Microsoft.AspNetCore.Components.Forms
@using MudBlazor
@using OpenArchival.Blazor.Config
@using OpenArchival.DataAccess.FileAccessManager;

<style>
    .file-upload-input {
        position: absolute;
        width: 100%;
        height: 100%;
        overflow: hidden;
        z-index: 10;
        opacity: 0;
    }
</style>

<MudStack Style="width: 100%">
    <MudFileUpload T="IReadOnlyList<IBrowserFile>"
                   @ref="@_fileUpload"
                   OnFilesChanged="OnInputFileChanged"
                   AppendMultipleFiles=true
                   MaximumFileCount="_options.Value.MaxFileCount"
                   Hidden="@false"
                   InputClass="file-upload-input"
                   tabindex="-1"
                   @ondrop="@ClearDragClass"
                   @ondragenter="@SetDragClass"
                   @ondragleave="@ClearDragClass"
                   @ondragend="@ClearDragClass">
        <ActivatorContent>
            <MudPaper Outlined="true"
                      Class="@_dragClass"
                      Style="height: 150px; display: flex; flex-direction: column; position: relative;">

                <div class="d-flex align-center justify-center pa-4" style="flex-shrink: 0;">
                    <MudText Typo="Typo.h6">
                        Drag and drop files here or click
                    </MudText>
                </div>

                            </MudPaper>
        </ActivatorContent>
    </MudFileUpload>
    @if (Files.Any() || ExistingFiles.Any())
    {
        <MudPaper Style="max-height: 150px; overflow-y: auto;" Outlined="true" Class="pa-4">
            @foreach (var file in Files)
            {
                var color = _fileToDiskFileName.Keys.Contains(file) ? Color.Success : Color.Warning;
                <MudChip T="string"
                         Color="@color"
                         Text="@file.Name"
                         tabindex="-1" />
            }

            @foreach (var filelisting in ExistingFiles)
            {
                 <MudChip T="string"
                         Color="@Color.Success"
                         Text="@filelisting.OriginalName"
                         tabindex="-1" />
            }
        </MudPaper>
    }

    <MudToolBar Gutters="@false"
                Class="relative d-flex justify-end gap-4">
        <MudButton Color="Color.Primary"
                   OnClick="@OpenFilePickerAsync"
                   Variant="Variant.Filled">
            Open file picker
        </MudButton>
        <MudButton Color="Color.Primary"
                   Disabled="@(!Files.Any())"
                   OnClick="@Upload"
                   Variant="Variant.Filled">
            Upload
        </MudButton>
        <MudButton Color="Color.Error"
                   Disabled="@(!Files.Any())"
                   OnClick="@ClearAsync"
                   Variant="Variant.Filled">
            Clear
        </MudButton>
    </MudToolBar>

    @if (Files.Count != _fileToDiskFileName.Count)
    {
        <MudText Color="Color.Error" Align="Align.Right">*Files must be uploaded</MudText>
    }
</MudStack>

@inject IOptions<FileUploadOptions> _options;
@inject IFilePathListingProvider PathProvider;
@inject ISnackbar Snackbar;
@inject ILogger<UploadDropBox> _logger;
@inject IDialogService DialogService;
@inject IFileAccessManager FileAccessManager;

    @code {
    private const string DefaultDragClass = "relative rounded-lg border-2 border-dashed pa-4 mt-4 mud-width-full mud-height-full";

    private string _dragClass = DefaultDragClass;

    public readonly List<IBrowserFile> Files = new();

    private readonly Dictionary<IBrowserFile, FilePathListing> _fileToDiskFileName = new();

    private MudFileUpload<IReadOnlyList<IBrowserFile>>? _fileUpload;

    public int SelectedFileCount { get => Files.Count; }

    public bool UploadsComplete { get; set; } = true;

    [Parameter]
    public EventCallback<List<FilePathListing>> FilesUploaded {get; set; } 

    [Parameter]
    public EventCallback ClearClicked { get; set; }

    [Parameter]
    public List<FilePathListing> ExistingFiles { get; set; } = new();

    protected override Task OnParametersSetAsync()
    {
        return base.OnParametersSetAsync();
    }

    private async Task ClearAsync()
    {

        bool? confirmed = await DialogService.ShowMessageBox
        (
            new MessageBoxOptions(){
            Message=$"Are you sure you want to clear files? You will loose all changes.", 
            Title="Clear Files", 
            CancelText="Cancel", 
            YesText="Clear"
        });

        if (confirmed is not null && (confirmed ?? throw new ArgumentNullException("confirmed was null")))
        {
            foreach (var pair in _fileToDiskFileName)
            {
                try
                {
                    await FileAccessManager.DeleteFileAsync(pair.Value.Id);
                }
                catch (Exception ex)
                {
                    _logger.LogError(ex, "Error deleting file {FileName}", pair.Key.Name);
                    Snackbar.Add($"Error cleaning up file: {pair.Key.Name}", Severity.Warning);
                }
            }

            foreach (var listing in ExistingFiles)
            {
                try
                {
                    await FileAccessManager.DeleteFileAsync(listing.Id);
                } 
                catch (Exception ex)
                {
                    _logger.LogError(ex, $"Error deleting file {listing.Path}");
                    Snackbar.Add($"Error cleaning up file: {listing.OriginalName}", Severity.Warning);
                }
            }

            _fileToDiskFileName.Clear();
            Files.Clear();
            await (_fileUpload?.ClearAsync() ?? Task.CompletedTask);

            ClearDragClass();
            UploadsComplete = true;
            await ClearClicked.InvokeAsync();

        }
    }

    private Task OpenFilePickerAsync()
        => _fileUpload?.OpenFilePickerAsync() ?? Task.CompletedTask;

    private async Task OnInputFileChanged(InputFileChangeEventArgs e)
    {
        ClearDragClass();
        var files = e.GetMultipleFiles(maximumFileCount: _options.Value.MaxFileCount);
        Files.AddRange(files);

        UploadsComplete = false;
        //StateHasChanged();
        await InvokeAsync(StateHasChanged);
    }


    private async Task Upload()
    {
        if (!Files.Any())
        {
            Snackbar.Add("No files to upload.", Severity.Warning);
            return;
        }

        Snackbar.Configuration.PositionClass = Defaults.Classes.Position.TopCenter;

        List<FilePathListing> fileListings = [];
        foreach (var file in Files)
        {
            if (_fileToDiskFileName.ContainsKey(file)) continue;
            try
            {

                var fileListing = await FileAccessManager.UploadFileAsync(file); 

                _fileToDiskFileName.Add(file, fileListing);

                fileListings.Add(fileListing);

                Snackbar.Add($"Uploaded {file.Name}", Severity.Success);

                UploadsComplete = true;
            }
            catch (Exception ex)
            {
                Snackbar.Add($"Error uploading file {file.Name}: {ex.Message}", Severity.Error);
            }
        }

        await FilesUploaded.InvokeAsync(fileListings);
    }

    private void SetDragClass()
        => _dragClass = $"{DefaultDragClass} mud-border-primary";

    private void ClearDragClass()
        => _dragClass = DefaultDragClass;

    public void RemoveFile(string filename)
    {
        var existingFile = ExistingFiles.Where(f => f.OriginalName == filename).FirstOrDefault();
        if (existingFile is not null)
        {
            ExistingFiles.Remove(existingFile);            
        } 

        var file = Files.Where(f => f.Name == filename).FirstOrDefault(); 
        if (file is not null)
        {
            Files.Remove(file);
        } 
    }
}