using KarleyLibrary.Erweiterungen;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Forms;
using Microsoft.AspNetCore.Components.Web;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using WK5.Core;
using WK5.Core.Basis;
using WK5_Blazor.CoreExtensions;

namespace WK5_Blazor.Components
{
    [Flags]
    public enum FilePermissions
    {
        Download = 1,
        Upload = 2,
        Deletion = 4,
        MultiSelect = 8
    }

    [Flags]
    public enum DirectoryPermissions
    {
        Creation = 1,
        Deletion = 2,
        MultiSelect = 4
    }

    public partial class FileBrowser
    {
        public string UID { get; private set; } = Guid.NewGuid().ToString();

        /*
         TODO: (Juri) Warnung wenn datei bereits existiert oder ein Fehler aufgetreten ist
         */

        [Parameter] public bool AllowAboveStart { get; set; } = false;
        [Parameter] public FilePermissions FilePermissions { get; set; } = FilePermissions.Upload;
        [Parameter] public DirectoryPermissions DirectoryPermissions { get; set; } = DirectoryPermissions.Creation;
        //[Parameter] public bool AllowUpload { get; set; } = true;
        //[Parameter] public bool AllowDirectoryCreation { get; set; } = true;

#nullable disable
        [Parameter] public string CurrentDirectory { get; set; }
#nullable enable

        [Parameter] public EventCallback<FileBrowserElement> FileSelected { get; set; }
        [Parameter] public EventCallback<FileBrowserElement> FileDeleted { get; set; }
        [Parameter] public EventCallback<FileBrowserElement> FileUploaded { get; set; }
        [Parameter] public string Search { get; set; } = String.Empty;

        private List<string> Dateien = new List<string>();
        private List<string> Ordner = new List<string>();

        private List<FileBrowserElement> BrowserElements = new List<FileBrowserElement>();
        private List<FileBrowserElement> DisplayElements = new List<FileBrowserElement>();
        private int Columns = 4;
        private int Rows = 0;
        private int MaxRows = 4;
        private int MaxColumns = 4;

        private int ItemsPerPage
        {
            get
            {
                return MaxColumns * MaxRows;
            }
        }

        private int Page = 1;

        private string? StartDirectory;

        public string ColumnClass
        {
            get
            {
                return $"col-md-{12 / MaxColumns}";
            }
        }


        private bool _uploading = false;
        private bool _downloading = false;

        private Task DeleteFileAsync(FileBrowserElement element) => DeleteFilesAsync(new List<FileBrowserElement> { element });

        private async Task DeleteFilesAsync(List<FileBrowserElement> elements)
        {
            if(elements.Count <= 0)
            {
                return;
            }

            if (!FilePermissions.HasFlag(FilePermissions.Deletion))
            {
                return;
            }

            string msg = $"Solle wirklich die {elements.Count} ausgewählten Dateien gelöscht werden?";
            if(elements.Count == 1)
            {
                var elem = elements.First();
                msg = $"Soll die Datei {new FileInfo(elem.Path).Name} wirklich gelöscht werden?";
            }
            else
            {
                if (!FilePermissions.HasFlag(FilePermissions.MultiSelect))
                {
                    return;
                }
            }

            bool confirmed = await jsRuntime.InvokeAsync<bool>("confirm", new object[] { msg });

            if (confirmed)
            {
                foreach(FileBrowserElement element in elements)
                {
                    if (element.BrowserType != FileBrowserType.Directory)
                    {
                        continue;
                    }

                    File.Delete(element.Path);
                    await ReloadCurrentDirectory();
                    GetPage(Page);
                }
            }

        }

        private Task DeleteDirectoryAsync(FileBrowserElement element) => DeleteDirectoriesAsync(new List<FileBrowserElement> { element });

        private async Task DeleteDirectoriesAsync(List<FileBrowserElement> elements)
        {
            if (elements.Count <= 0)
            {
                return;
            }

            if (!FilePermissions.HasFlag(DirectoryPermissions.Deletion))
            {
                return;
            }

            string msg = $"Sollen wirklich die {elements.Count} ausgewählten Verzeichnisse gelöscht werden?";
            if(elements.Count == 1)
            {
                var elem = elements.First();
                msg = $"Soll das Verzeichnis {new DirectoryInfo(elem.Path).Name} wirklich gelöscht werden?";
            }
            else
            {
                if (!FilePermissions.HasFlag(DirectoryPermissions.MultiSelect))
                {
                    return;
                }
            }
            bool confirmed = await jsRuntime.InvokeAsync<bool>("confirm", new object[] { msg });

            if (confirmed)
            {
                foreach(FileBrowserElement element in elements)
                {
                    if(element.BrowserType != FileBrowserType.Directory)
                    {
                        continue;
                    }

                    Directory.Delete(element.Path, true);
                    await ReloadCurrentDirectory();
                    GetPage(Page);
                }
            }
        }

        protected override async Task OnParametersSetAsync()
        {
            CurrentDirectory = Path.GetFullPath(CurrentDirectory).TrimEnd(new[] { '/', '\\' });

            StartDirectory = CurrentDirectory;
            await ReloadCurrentDirectory();
            GetPage(Page);
        }
        private Task ReloadCurrentDirectory()
        {
            BrowserElements.Clear();



            bool upDir = false;
            if (CurrentDirectory.TrimEnd(new[] { '/', '\\' }).Equals(StartDirectory.TrimEnd(new[] { '/', '\\' }), StringComparison.OrdinalIgnoreCase))
            {
                if (AllowAboveStart)
                {
                    upDir = true;
                }
            }
            else
            {
                upDir = true;
            }


            if (upDir)
            {
                BrowserElements.Add(new FileBrowserElement()
                {
                    BrowserType = FileBrowserType.Directory,
                    Path = Directory.GetParent(CurrentDirectory)?.FullName ?? String.Empty,
                    Name = "..",
                    IsDirectoryUp = true
                });
            }

            return Task.Run(() =>
            {
                foreach (string folder in Directory.GetDirectories(CurrentDirectory))
                {
                    BrowserElements.Add(new FileBrowserElement()
                    {
                        BrowserType = FileBrowserType.Directory,
                        Path = folder
                    });
                }

                foreach (string file in Directory.GetFiles(CurrentDirectory))
                {
                    BrowserElements.Add(new FileBrowserElement()
                    {
                        BrowserType = FileBrowserType.File,
                        Path = file,
                        fileInfo = new FileInfo(file)
                    });
                }
            });
        }

        private async Task KeyUp(KeyboardEventArgs e)
        {
            if (e.Key.Equals("Enter", StringComparison.OrdinalIgnoreCase))
            {
                await ContentSearch();
            }
        }

        private void GetPage(int page)
        {
            DisplayElements.Clear();

            var matchingElements = BrowserElements;
            if (!String.IsNullOrWhiteSpace(Search))
            {
                matchingElements = BrowserElements.Where(x => x.Name.Contains(Search, StringComparison.OrdinalIgnoreCase) || x.Name.Equals("..")).ToList();
            }



            int itemsPerPage = MaxColumns * MaxRows;
            int from = matchingElements.Count < itemsPerPage ? 0 : (page - 1) * itemsPerPage;


            int itemCount = from + itemsPerPage > matchingElements.Count ? matchingElements.Count - from : itemsPerPage;

            DisplayElements = matchingElements.GetRange(from, itemCount);
            Rows = (int)Math.Ceiling(DisplayElements.Count / (decimal)MaxColumns);
            Columns = DisplayElements.Count > MaxColumns ? MaxColumns : DisplayElements.Count;
        }

        private int GetPageAmount()
        {
            var matchingElements = BrowserElements;
            if (!String.IsNullOrWhiteSpace(Search))
            {
                matchingElements = BrowserElements.Where(x => x.Name.Contains(Search, StringComparison.OrdinalIgnoreCase) || x.Name.Equals("..")).ToList();
            }
            return matchingElements.Count;
        }

        private bool ShowContextMenu(FileBrowserElement element)
        {
            if (element.BrowserType == FileBrowserType.Directory)
            {
                return DirectoryPermissions.HasFlag(DirectoryPermissions.Deletion);
            }
            else if (element.BrowserType == FileBrowserType.File)
            {
                return FilePermissions.HasFlag(FilePermissions.Download) || FilePermissions.HasFlag(FilePermissions.Deletion);
            }
            else
            {
                return false;
            }
        }

        private async Task OnClick(FileBrowserElement element)
        {
            if (element.BrowserType == FileBrowserType.Directory)
            {
                CurrentDirectory = element.Path;
                await ReloadCurrentDirectory();
                Page = 1;
                GetPage(Page);
            }
            else if (element.BrowserType == FileBrowserType.File)
            {
                await FileSelected.InvokeAsync(element);
            }

        }
        private async Task CreateFolder()
        {
            if (!DirectoryPermissions.HasFlag(DirectoryPermissions.Creation))
            {
                return;
            }

            string name = await jsRuntime.InvokeAsync<string>(
            "prompt", new object[] {
            "Bitte einen Namen für den Ordner angeben",
            ""});
            if (String.IsNullOrWhiteSpace(name) || name.Where(x => Path.GetInvalidPathChars().Contains(x)).Any())
            {

            }
            else
            {
                string newFolder = Path.Combine(CurrentDirectory, name);
                Directory.CreateDirectory(newFolder);
                await ReloadCurrentDirectory();
                Page = 1;
                GetPage(Page);
            }
        }

        private async Task UploadFile()
        {
            if (!FilePermissions.HasFlag(FilePermissions.Upload))
            {
                return;
            }

            await jsRuntime.InvokeAsync<string>("eval", new object[] { $"document.getElementById('file-browser-input-file').click();" });
        }

        private Task DownloadFile(FileBrowserElement element) => DownloadFiles(new List<FileBrowserElement> { element });

        private async Task DownloadFiles(List<FileBrowserElement> elements)
        {
            _downloading = true;
            StateHasChanged();
            if (!FilePermissions.HasFlag(FilePermissions.Download))
            {
                return;
            }

            foreach(FileBrowserElement element in elements)
            {
                if (element.BrowserType != FileBrowserType.File)
                {
                    continue;
                }

                byte[] data = await File.ReadAllBytesAsync(element.Path);
                await downloadService.DownloadFile(element.Name, data, "application/octet-stream");                
            }
            _downloading = false;
        }        

        private async Task HandleFileSelected(InputFileChangeEventArgs e)
        {
            _uploading = true;
            foreach (IBrowserFile upload in e.GetMultipleFiles(Int32.MaxValue))
            {
                byte[] data = Array.Empty<byte>();
                try
                {
                    data = await upload.ToArray();
                }
                catch (Exception ex)
                {
                    if (ex is IOException)
                    {
                        await AddAlertAsync(new AlertBox($"Die Datei ist zu groß!", AlertType.Danger));
                    }
                    else
                    {
                        await AddAlertAsync(new AlertBox($"Es ist ein Fehler beim Dateiupload aufgetreten! Fehler: {ex.Message}", AlertType.Danger));
                    }

                }

                string path = Path.Combine(CurrentDirectory, upload.Name);

                bool findSuitableName = true;
                if (File.Exists(path))
                {
                    findSuitableName = !(await jsRuntime.InvokeAsync<bool>("confirm", new object[] { $"Die Datei {Path.GetFileName(path)} existiert bereits. Soll die Datei überschrieben werden?" }));  
                }

                if (findSuitableName)
                {
                    var (success, file, message) = FileErweiterung.FindSuitableFileName(path);
                    if (success)
                    {
                        await File.WriteAllBytesAsync(file, data);
                    }
                }
                else
                {
                    await File.WriteAllBytesAsync(path, data);
                }                
            }


            _uploading = false;
            await ReloadCurrentDirectory();
            Page = 1;
            GetPage(Page);

        }

        private Task SeiteChange(int page)
        {
            //await ReloadCurrentDirectory();
            Page = page;
            GetPage(Page);
            return Task.CompletedTask;
        }


        private string GetFileIcon(string filename)
        {
            string ext = Path.GetExtension(filename).ToLower();

            return ext switch
            {
                ".doc" or ".docx" => "fa-file-word",
                ".ppt" or ".pptx" => "fa-file-powerpoint",
                ".pdf" => "fa-file-pdf",
                ".jpg" or ".jpeg" or ".png" or ".gif" => "fa-file-image",
                ".xlsx" or ".xls" => "fa-file-excel",
                ".csv" => "fa-file-csv",
                ".zip" or ".7z" or ".rar" or ".tar" or ".gz" or ".tar.gz" => "fa-file-archive",
                ".mp3" or ".wav" => "fa-file-audio",
                ".mp4" => "fa-file-video",
                _ => "fa-file",
            };
        }

        private bool IsImage(string filename)
        {
            string ext = Path.GetExtension(filename).ToLower();
            return ext is ".jpg" or ".jpeg" or ".png" or ".gif";
        }

        private string GetPreviewBase64(string file)
        {
            if (!File.Exists(file))
            {
                return String.Empty;
            }

            byte[] bytes = File.ReadAllBytes(file);
            return Convert.ToBase64String(bytes);
        }

        private Task ContentSearch()
        {
            GetPage(Page);
            return Task.CompletedTask;
        }
    }

    public class FileBrowserElement
    {
        public FileInfo? fileInfo { get; set; }
        public FileBrowserType BrowserType { get; set; }
        public string Path { get; set; } = String.Empty;
        private string _name = String.Empty;
        public bool IsDirectoryUp { get; set; } = false;
        public string Name
        {
            get
            {

                return String.IsNullOrWhiteSpace(_name) ? System.IO.Path.GetFileName(Path) : _name;
            }

            set
            {
                _name = value;
            }
        }

        public string WebPath
        {
            get
            {
                return '/' + Path.Replace(GlobalConfig.WWWRoot, String.Empty).TrimStart('/').TrimStart('\\');
            }
        }

        public bool Selected { get; set; } = false;

    }

    public enum FileBrowserType
    {
        File,
        Directory
    }
}
