using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Oqtane.Models;
using Oqtane.Shared;
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using Oqtane.Security;
using System.Linq;
using System.Net;
using Oqtane.Enums;
using Oqtane.Infrastructure;
using Oqtane.Repository;
using Oqtane.Extensions;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Processing;
using SixLabors.ImageSharp.Formats.Png;
using System.Net.Http;
using Microsoft.AspNetCore.Cors;

// ReSharper disable StringIndexOfIsCultureSpecific.1

namespace Oqtane.Controllers
{
    [Route(ControllerRoutes.ApiRoute)]
    public class FileController : Controller
    {
        private readonly IWebHostEnvironment _environment;
        private readonly IFileRepository _files;
        private readonly IFolderRepository _folders;
        private readonly IUserPermissions _userPermissions;
        private readonly ISyncManager _syncManager;
        private readonly ILogManager _logger;
        private readonly Alias _alias;
        private readonly ISettingRepository _settingRepository;
        public FileController(IWebHostEnvironment environment, IFileRepository files, IFolderRepository folders, IUserPermissions userPermissions, ISettingRepository settingRepository, ISyncManager syncManager, ILogManager logger, ITenantManager tenantManager)
        {
            _environment = environment;
            _files = files;
            _folders = folders;
            _userPermissions = userPermissions;
            _syncManager = syncManager;
            _logger = logger;
            _alias = tenantManager.GetAlias();
            _settingRepository = settingRepository;
        }

        // GET: api/<controller>?folder=x
        [HttpGet]
        public IEnumerable<Models.File> Get(string folder)
        {
            List<Models.File> files = new List<Models.File>();
            int folderid;
            if (int.TryParse(folder, out folderid))
            {
                Folder Folder = _folders.GetFolder(folderid);
                if (Folder != null && Folder.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User, PermissionNames.Browse, Folder.PermissionList))
                {
                    files = _files.GetFiles(folderid).ToList();
                }
                else
                {
                    _logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized File Get Attempt {FolderId}", folder);
                    HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
                    files = null;
                }
            }
            else
            {
                if (User.IsInRole(RoleNames.Host))
                {
                    folder = GetFolderPath(folder);
                    if (Directory.Exists(folder))
                    {
                        foreach (string file in Directory.GetFiles(folder))
                        {
                            files.Add(new Models.File { Name = Path.GetFileName(file), Extension = Path.GetExtension(file)?.Replace(".", "") });
                        }
                    }
                }
                else
                {
                    _logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized File Get Attempt {Folder}", folder);
                    HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
                    files = null;
                }
            }

            return files;
        }

        // GET: api/<controller>/siteId/folderPath
        [HttpGet("{siteId}/{path}")]
        public IEnumerable<Models.File> Get(int siteId, string path)
        {
            List<Models.File> files;

            Folder folder = _folders.GetFolder(siteId, WebUtility.UrlDecode(path));
            if (folder != null && folder.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User, PermissionNames.Browse, folder.PermissionList))
            {
                files = _files.GetFiles(folder.FolderId).ToList();
            }
            else
            {
                _logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized File Get Attempt {SiteId} {Path}", siteId, path);
                HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
                files = null;
            }

            return files;
        }

        // GET api/<controller>/5
        [HttpGet("{id}")]
        public Models.File Get(int id)
        {
            Models.File file = _files.GetFile(id);
            if (file != null && file.Folder.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User, PermissionNames.View, file.Folder.PermissionList))
            {
                return file;
            }
            else
            {
                if (file != null)
                {
                    _logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized File Get Attempt {FileId}", id);
                    HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
                }
                else
                {
                    HttpContext.Response.StatusCode = (int)HttpStatusCode.NotFound;
                }
                return null;
            }
        }

        [HttpGet("name/{name}/{folderId}")]
        public Models.File Get(string name, int folderId)
        {
            Models.File file = _files.GetFile(folderId, name);
            if (file != null && file.Folder.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User, PermissionNames.View, file.Folder.PermissionList))
            {
                return file;
            }
            else
            {
                if (file != null)
                {
                    _logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized File Get Attempt {Name} For Folder {FolderId}", name, folderId);
                    HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
                }
                else
                {
                    HttpContext.Response.StatusCode = (int)HttpStatusCode.NotFound;
                }
                return null;
            }
        }

        // POST api/<controller>
        [HttpPost]
        [Authorize(Roles = RoleNames.Registered)]
        public Models.File Post([FromBody] Models.File file)
        {
            var folder = _folders.GetFolder(file.FolderId);
            if (ModelState.IsValid && folder != null && folder.SiteId == _alias.SiteId)
            {
                if (_userPermissions.IsAuthorized(User, folder.SiteId, EntityNames.Folder, file.FolderId, PermissionNames.Edit))
                {
                    var filepath = _files.GetFilePath(file);
                    if (System.IO.File.Exists(filepath))
                    {
                        file = CreateFile(file.Name, folder.FolderId, filepath);
                        file = _files.AddFile(file);
                        _syncManager.AddSyncEvent(_alias.TenantId, EntityNames.File, file.FileId, SyncEventActions.Create);
                        _logger.Log(LogLevel.Information, this, LogFunction.Create, "File Added {File}", file);
                    }
                    else
                    {
                        _logger.Log(LogLevel.Error, this, LogFunction.Security, "File Does Not Exist At Path {FilePath}", filepath);
                        HttpContext.Response.StatusCode = (int)HttpStatusCode.NotFound;
                        file = null;
                    }
                }
                else
                {
                    _logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized File Post Attempt {File}", file);
                    HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
                    file = null;
                }
            }
            else
            {
                _logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized File Post Attempt {File}", file);
                HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
                file = null;
            }

            return file;
        }

        // PUT api/<controller>/5
        [HttpPut("{id}")]
        [Authorize(Roles = RoleNames.Registered)]
        public Models.File Put(int id, [FromBody] Models.File file)
        {
            var File = _files.GetFile(file.FileId, false);
            if (ModelState.IsValid && file.Folder.SiteId == _alias.SiteId && file.FileId == id && File != null // ensure file exists
                && _userPermissions.IsAuthorized(User, file.Folder.SiteId, EntityNames.Folder, File.FolderId, PermissionNames.Edit) // ensure user had edit rights to original folder
                && _userPermissions.IsAuthorized(User, file.Folder.SiteId, EntityNames.Folder, file.FolderId, PermissionNames.Edit)) // ensure user has edit rights to new folder
            {
                if (File.Name != file.Name || File.FolderId != file.FolderId)
                {
                    file.Folder = _folders.GetFolder(file.FolderId, false);
                    string folderpath = _folders.GetFolderPath(file.Folder);
                    if (!Directory.Exists(folderpath))
                    {
                        Directory.CreateDirectory(folderpath);
                    }
                    System.IO.File.Move(_files.GetFilePath(File), Path.Combine(folderpath, file.Name));
                }

                var newfile = CreateFile(File.Name, file.Folder.FolderId, _files.GetFilePath(file));
                if (newfile != null)
                {
                    file.Extension = newfile.Extension;
                    file.Size = newfile.Size;
                    file.ImageWidth = newfile.ImageWidth;
                    file.ImageHeight = newfile.ImageHeight;
                }

                file = _files.UpdateFile(file);
                _syncManager.AddSyncEvent(_alias.TenantId, EntityNames.File, file.FileId, SyncEventActions.Update);
                _logger.Log(LogLevel.Information, this, LogFunction.Update, "File Updated {File}", file);
            }
            else
            {
                _logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized File Put Attempt {File}", file);
                HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
                file = null;
            }

            return file;
        }

        // DELETE api/<controller>/5
        [HttpDelete("{id}")]
        [Authorize(Roles = RoleNames.Registered)]
        public void Delete(int id)
        {
            Models.File file = _files.GetFile(id);
            if (file != null && file.Folder.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User, file.Folder.SiteId, EntityNames.Folder, file.Folder.FolderId, PermissionNames.Edit))
            {
                string filepath = _files.GetFilePath(file);
                if (System.IO.File.Exists(filepath))
                {
                    // remove file and thumbnails
                    foreach(var f in Directory.GetFiles(Path.GetDirectoryName(filepath), Path.GetFileNameWithoutExtension(filepath) + ".*"))
                    {
                        System.IO.File.Delete(f);
                    }
                }

                _files.DeleteFile(id);
                _syncManager.AddSyncEvent(_alias.TenantId, EntityNames.File, file.FileId, SyncEventActions.Delete);
                _logger.Log(LogLevel.Information, this, LogFunction.Delete, "File Deleted {File}", file);
            }
            else
            {
                _logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized File Delete Attempt {FileId}", id);
                HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
            }
        }

        // GET api/<controller>/upload?url=x&folderid=y&name=z
        [HttpGet("upload")]
        public async Task<Models.File> UploadFile(string url, string folderid, string name)
        {
            Models.File file = null;

            Folder folder = null;
            int FolderId;
            if (int.TryParse(folderid, out FolderId))
            {
                folder = _folders.GetFolder(FolderId);
            }

            var _UploadableFiles = _settingRepository.GetSetting(EntityNames.Site, _alias.SiteId, "UploadableFiles")?.SettingValue;
            _UploadableFiles = (string.IsNullOrEmpty(_UploadableFiles)) ? Constants.UploadableFiles : _UploadableFiles;

            if (folder != null && folder.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User, PermissionNames.Edit, folder.PermissionList))
            {
                string folderPath = _folders.GetFolderPath(folder);
                CreateDirectory(folderPath);

                if (string.IsNullOrEmpty(name))
                {
                    name = url.Substring(url.LastIndexOf("/", StringComparison.Ordinal) + 1);
                }
                // check for allowable file extensions
                if (!_UploadableFiles.Split(',').Contains(Path.GetExtension(name).ToLower().Replace(".", "")))
                {
                    _logger.Log(LogLevel.Error, this, LogFunction.Create, "File Could Not Be Downloaded From Url Due To Its File Extension {Url}", url);
                    HttpContext.Response.StatusCode = (int)HttpStatusCode.Conflict;
                    return file;
                }

                if (!name.IsPathOrFileValid())
                {
                    _logger.Log(LogLevel.Error, this, LogFunction.Create, $"File Could Not Be Downloaded From Url Due To Its File Name Not Allowed {url}");
                    HttpContext.Response.StatusCode = (int)HttpStatusCode.Conflict;
                    return file;
                }

                try
                {
                    string targetPath = Path.Combine(folderPath, name);

                    // remove file if it already exists
                    if (System.IO.File.Exists(targetPath))
                    {
                        System.IO.File.Delete(targetPath);
                    }

                    using (var client = new HttpClient())
                    {
                        using (var stream = await client.GetStreamAsync(url))
                        {
                            using (var fileStream = new FileStream(targetPath, FileMode.CreateNew))
                            {
                                await stream.CopyToAsync(fileStream);
                            }
                        }
                    }

                    file = CreateFile(name, folder.FolderId, targetPath);
                    if (file != null)
                    {
                        file = _files.AddFile(file);
                        _syncManager.AddSyncEvent(_alias.TenantId, EntityNames.File, file.FileId, SyncEventActions.Create);
                    }
                }
                catch (Exception ex)
                {
                    _logger.Log(LogLevel.Error, this, LogFunction.Create, ex, "File Could Not Be Downloaded From Url {Url} {Error}", url, ex.Message);
                }
            }
            else
            {
                _logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized File Download Attempt {FolderId} {Url}", folderid, url);
                HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
            }

            return file;
        }

        // POST api/<controller>/upload
        [EnableCors(Constants.MauiCorsPolicy)]
        [HttpPost("upload")]
        public async Task UploadFile(string folder, IFormFile formfile)
        {
            if (formfile == null || formfile.Length <= 0)
            {
                return;
            }

            // Get the UploadableFiles extensions
            string _UploadableFiles = _settingRepository.GetSetting(EntityNames.Site, _alias.SiteId, "UploadableFiles")?.SettingValue;
            _UploadableFiles = (string.IsNullOrEmpty(_UploadableFiles)) ? Constants.UploadableFiles : _UploadableFiles;

            // ensure filename is valid
            string token = ".part_";
            if (!formfile.FileName.IsPathOrFileValid() || !formfile.FileName.Contains(token))
            {
                return;
            }

            // check for allowable file extensions (ignore token)
            var extension = Path.GetExtension(formfile.FileName.Substring(0, formfile.FileName.IndexOf(token))).Replace(".", "");
            if (!_UploadableFiles.Split(',').Contains(extension.ToLower()))
            {
                return;
            }

            string folderPath = "";

            int FolderId;
            if (int.TryParse(folder, out FolderId))
            {
                Folder Folder = _folders.GetFolder(FolderId);
                if (Folder != null && Folder.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User, PermissionNames.Edit, Folder.PermissionList))
                {
                    folderPath = _folders.GetFolderPath(Folder);
                }
            }
            else
            {
                FolderId = -1;
                if (User.IsInRole(RoleNames.Host))
                {
                    folderPath = GetFolderPath(folder);
                }
            }

            if (!string.IsNullOrEmpty(folderPath))
            {
                CreateDirectory(folderPath);
                using (var stream = new FileStream(Path.Combine(folderPath, formfile.FileName), FileMode.Create))
                {
                    await formfile.CopyToAsync(stream);
                }

                string upload = await MergeFile(folderPath, formfile.FileName);
                if (upload != "" && FolderId != -1)
                {
                    var file = CreateFile(upload, FolderId, Path.Combine(folderPath, upload));
                    if (file != null)
                    {
                        if (file.FileId == 0)
                        {
                            file = _files.AddFile(file);
                        }
                        else
                        {
                            file = _files.UpdateFile(file);
                        }
                        _logger.Log(LogLevel.Information, this, LogFunction.Create, "File Upload Succeeded {File}", Path.Combine(folderPath, upload));
                        _syncManager.AddSyncEvent(_alias.TenantId, EntityNames.File, file.FileId, SyncEventActions.Create);
                    }
                }
            }
            else
            {
                _logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized File Upload Attempt {Folder} {File}", folder, formfile.FileName);
                HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
            }
        }

        private async Task<string> MergeFile(string folder, string filename)
        {
            string merged = "";

            // parse the filename which is in the format of filename.ext.part_001_999
            string token = ".part_";
            string parts = Path.GetExtension(filename)?.Replace(token, ""); // returns "001_999"
            int totalparts = int.Parse(parts?.Substring(parts.IndexOf("_") + 1));

            filename = Path.GetFileNameWithoutExtension(filename); // base filename
            string[] fileparts = Directory.GetFiles(folder, filename + token + "*"); // list of all file parts

            // if all of the file parts exist ( note that file parts can arrive out of order )
            if (fileparts.Length == totalparts && CanAccessFiles(fileparts))
            {
                // merge file parts into temp file ( in case another user is trying to get the file )
                bool success = true;
                using (var stream = new FileStream(Path.Combine(folder, filename + ".tmp"), FileMode.Create))
                {
                    foreach (string filepart in fileparts)
                    {
                        try
                        {
                            using (FileStream chunk = new FileStream(filepart, FileMode.Open))
                            {
                                await chunk.CopyToAsync(stream);
                            }
                        }
                        catch
                        {
                            success = false;
                        }
                    }
                }

                // clean up file parts
                foreach (var file in Directory.GetFiles(folder, "*" + token + "*"))
                {
                    // file name matches part or is more than 2 hours old (ie. a prior file upload failed)
                    if (fileparts.Contains(file) || System.IO.File.GetCreationTime(file).ToUniversalTime() < DateTime.UtcNow.AddHours(-2))
                    {
                        System.IO.File.Delete(file);
                    }
                }

                // rename temp file
                if (success)
                {
                    // remove file if it already exists (as well as any thumbnails)
                    foreach (var file in Directory.GetFiles(folder, Path.GetFileNameWithoutExtension(filename) + ".*"))
                    {
                        if (Path.GetExtension(file) != ".tmp")
                        {
                            System.IO.File.Delete(file);
                        }
                    }

                    // rename temp file now that the entire process is completed
                    System.IO.File.Move(Path.Combine(folder, filename + ".tmp"), Path.Combine(folder, filename));

                    // return filename
                    merged = filename;
                }
            }

            return merged;
        }

        private bool CanAccessFiles(string[] files)
        {
            // ensure files are not locked by another process
            FileStream stream = null;
            bool locked = false;
            foreach (string file in files)
            {
                locked = true;
                int attempts = 0;
                // note that this will wait a maximum of 15 seconds for a file to become unlocked
                while (attempts < 5 && locked)
                {
                    try
                    {
                        stream = System.IO.File.Open(file, FileMode.Open, FileAccess.Read, FileShare.None);
                        locked = false;
                    }
                    catch // file is locked by another process
                    {
                        attempts += 1;
                        Thread.Sleep(1000 * attempts); // progressive retry
                    }
                    finally
                    {
                        if (stream != null)
                        {
                            stream.Close();
                        }
                    }
                }
                if (locked)
                {
                    break; // file still locked after retrying
                }
            }
            return !locked;
        }

        /// <summary>
        /// Get file with header
        /// Content-Disposition: inline
        /// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Disposition
        /// </summary>
        /// <param name="id">File Id from Oqtane filesystem </param>
        /// <returns>file content</returns>

        // GET api/<controller>/download/5
        [HttpGet("download/{id}")]
        public IActionResult DownloadInline(int id)
        {
            return Download(id, false);
        }
        /// <summary>
        /// Get file with header
        /// Content-Disposition: attachment; filename="filename.jpg"
        /// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Disposition
        ///
        /// </summary>
        /// <param name="id">File Id from Oqtane filesystem</param>
        /// <returns></returns>

        // GET api/<controller>/download/5/attach
        [HttpGet("download/{id}/attach")]
        public IActionResult DownloadAttachment(int id)
        {
            return Download(id, true);
        }

        private IActionResult Download(int id, bool asAttachment)
        {
            var file = _files.GetFile(id);
            if (file != null && file.Folder.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User, PermissionNames.View, file.Folder.PermissionList))
            {
                var filepath = _files.GetFilePath(file);
                if (System.IO.File.Exists(filepath))
                {
                    if (asAttachment)
                    {
                        _syncManager.AddSyncEvent(_alias.TenantId, EntityNames.File, file.FileId, "Download");
                        return PhysicalFile(filepath, file.GetMimeType(), file.Name);
                    }
                    else
                    {
                        return PhysicalFile(filepath, file.GetMimeType());
                    }
                }
                else
                {
                    _logger.Log(LogLevel.Error, this, LogFunction.Read, "File Does Not Exist {FileId} {FilePath}", id, filepath);
                    HttpContext.Response.StatusCode = (int)HttpStatusCode.NotFound;
                }
            }
            else
            {
                _logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized File Access Attempt {FileId}", id);
                HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
            }

            string errorPath = Path.Combine(GetFolderPath("wwwroot/images"), "error.png");
            return System.IO.File.Exists(errorPath) ? PhysicalFile(errorPath, MimeUtilities.GetMimeType(errorPath)) : null;
        }

        [HttpGet("image/{id}/{width}/{height}/{mode}/{position}/{background}/{rotate}/{recreate}")]
        public IActionResult GetImage(int id, int width, int height, string mode, string position, string background, string rotate, string recreate)
        {
            var file = _files.GetFile(id);

            var _ImageFiles = _settingRepository.GetSetting(EntityNames.Site, _alias.SiteId, "ImageFiles")?.SettingValue;
            _ImageFiles = (string.IsNullOrEmpty(_ImageFiles)) ? Constants.ImageFiles : _ImageFiles;

            if (file != null && file.Folder.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User, PermissionNames.View, file.Folder.PermissionList))
            {
                if (_ImageFiles.Split(',').Contains(file.Extension.ToLower()))
                {
                    var filepath = _files.GetFilePath(file);
                    if (System.IO.File.Exists(filepath))
                    {
                        // validation
                        if (!Enum.TryParse(mode, true, out ResizeMode _)) mode = "crop";
                        if (!Enum.TryParse(position, true, out AnchorPositionMode _)) position = "center";
                        if (!Color.TryParseHex("#" + background, out _)) background = "transparent";
                        if (!int.TryParse(rotate, out _)) rotate = "0";
                        rotate = (int.Parse(rotate) < 0 || int.Parse(rotate) > 360) ? "0" : rotate;
                        if (!bool.TryParse(recreate, out _)) recreate = "false";

                        string imagepath = filepath.Replace(Path.GetExtension(filepath), "." + width.ToString() + "x" + height.ToString() + ".png");
                        if (!System.IO.File.Exists(imagepath) || bool.Parse(recreate))
                        {
                            // user has edit access to folder or folder supports the image size being created
                            if (_userPermissions.IsAuthorized(User, PermissionNames.Edit, file.Folder.PermissionList) ||
                              (!string.IsNullOrEmpty(file.Folder.ImageSizes) && (file.Folder.ImageSizes == "*" || file.Folder.ImageSizes.ToLower().Split(",").Contains(width.ToString() + "x" + height.ToString()))))
                            {
                                imagepath = CreateImage(filepath, width, height, mode, position, background, rotate, imagepath);
                            }
                            else
                            {
                                _logger.Log(LogLevel.Error, this, LogFunction.Security, "Invalid Image Size For Folder {Folder} {Width} {Height}", file.Folder, width, height);
                                HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
                            }
                        }
                        if (!string.IsNullOrEmpty(imagepath))
                        {
                            return PhysicalFile(imagepath, file.GetMimeType());
                        }
                        else
                        {
                            _logger.Log(LogLevel.Error, this, LogFunction.Create, "Error Displaying Image For File {File} {Width} {Height}", file, width, height);
                            HttpContext.Response.StatusCode = (int)HttpStatusCode.NotFound;
                        }
                    }
                    else
                    {
                        _logger.Log(LogLevel.Error, this, LogFunction.Read, "File Does Not Exist {FileId} {FilePath}", id, filepath);
                        HttpContext.Response.StatusCode = (int)HttpStatusCode.NotFound;
                    }
                }
                else
                {
                    _logger.Log(LogLevel.Error, this, LogFunction.Security, "File Is Not An Image {File}", file);
                    HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
                }
            }
            else
            {
                if (file != null)
                {
                    _logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized File Access Attempt {FileId}", id);
                    HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
                }
                else
                {
                    HttpContext.Response.StatusCode = (int)HttpStatusCode.NotFound;
                }
            }

            string errorPath = Path.Combine(GetFolderPath("wwwroot/images"), "error.png");
            return System.IO.File.Exists(errorPath) ? PhysicalFile(errorPath, MimeUtilities.GetMimeType(errorPath)) : null;
        }

        private string CreateImage(string filepath, int width, int height, string mode, string position, string background, string rotate, string imagepath)
        {
            try
            {
                using (var stream = new FileStream(filepath, FileMode.Open, FileAccess.Read))
                {
                    stream.Position = 0;
                    using (var image = Image.Load(stream))
                    {
                        int.TryParse(rotate, out int angle);
                        Enum.TryParse(mode, true, out ResizeMode resizemode);
                        Enum.TryParse(position, true, out AnchorPositionMode anchorpositionmode);

                        PngEncoder encoder;

                        if (background != "transparent")
                        {
                            image.Mutate(x => x
                                .AutoOrient() // auto orient the image
                                .Rotate(angle)
                                .Resize(new ResizeOptions
                                {
                                    Mode = resizemode,
                                    Position = anchorpositionmode,
                                    Size = new Size(width, height),
                                    PadColor = Color.ParseHex("#" + background)
                                }));

                            encoder = new PngEncoder();
                        }
                        else
                        {
                            image.Mutate(x => x
                                .AutoOrient() // auto orient the image
                                .Rotate(angle)
                                .Resize(new ResizeOptions
                                {
                                    Mode = resizemode,
                                    Position = anchorpositionmode,
                                    Size = new Size(width, height)
                                }));

                            encoder = new PngEncoder
                            {
                                ColorType = PngColorType.RgbWithAlpha,
                                TransparentColorMode = PngTransparentColorMode.Preserve,
                                BitDepth = PngBitDepth.Bit8,
                                CompressionLevel = PngCompressionLevel.BestSpeed
                            };
                        }

                        image.Save(imagepath, encoder);
                    }
                }
            }
            catch (Exception ex)
            {
                _logger.Log(LogLevel.Error, this, LogFunction.Security, ex, "Error Creating Image For File {FilePath} {Width} {Height} {Mode} {Rotate} {Error}", filepath, width, height, mode, rotate, ex.Message);
                imagepath = "";
            }

            return imagepath;
        }

        private string GetFolderPath(string folder)
        {
            return Utilities.PathCombine(_environment.ContentRootPath, folder);
        }

        private void CreateDirectory(string folderpath)
        {
            if (!Directory.Exists(folderpath))
            {
                string path = "";
                var separators = new char[] { Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar };
                string[] folders = folderpath.Split(separators, StringSplitOptions.RemoveEmptyEntries);
                foreach (string folder in folders)
                {
                    path = Utilities.PathCombine(path, folder, Path.DirectorySeparatorChar.ToString());
                    if (!Directory.Exists(path))
                    {
                        try
                        {
                            Directory.CreateDirectory(path);
                        }
                        catch (Exception ex)
                        {
                            _logger.Log(LogLevel.Error, this, LogFunction.Create, ex, "Unable To Create Folder {Folder}", path);
                        }
                    }
                }
            }
        }

        private Models.File CreateFile(string filename, int folderid, string filepath)
        {
            var file = _files.GetFile(folderid, filename);

            var _ImageFiles = _settingRepository.GetSetting(EntityNames.Site, _alias.SiteId, "ImageFiles")?.SettingValue;
            _ImageFiles = (string.IsNullOrEmpty(_ImageFiles)) ? Constants.ImageFiles : _ImageFiles;

            int size = 0;
            var folder = _folders.GetFolder(folderid, false);
            if (folder.Capacity != 0)
            {
                foreach (var f in _files.GetFiles(folderid, false))
                {
                    size += f.Size;
                }
            }

            FileInfo fileinfo = new FileInfo(filepath);
            if (folder.Capacity == 0 || ((size + fileinfo.Length) / 1000000) < folder.Capacity)
            {
                if (file == null)
                {
                    file = new Models.File();
                }
                file.Name = filename;
                file.FolderId = folderid;

                file.Extension = fileinfo.Extension.ToLower().Replace(".", "");
                file.Size = (int)fileinfo.Length;
                file.ImageHeight = 0;
                file.ImageWidth = 0;

                if (_ImageFiles.Split(',').Contains(file.Extension.ToLower()))
                {
                    try
                    {
                        FileStream stream = new FileStream(filepath, FileMode.Open, FileAccess.Read);
                        using (Image image = Image.Load(stream))
                        {
                            file.ImageHeight = image.Height;
                            file.ImageWidth = image.Width;
                        }
                        stream.Close();
                    }
                    catch
                    {
                        // error opening image file
                    }
                }
            }
            else
            {
                System.IO.File.Delete(filepath);
                if (file != null)
                {
                    _files.DeleteFile(file.FileId);
                }
                _logger.Log(LogLevel.Warning, this, LogFunction.Create, "File Exceeds Folder Capacity And Has Been Removed {Folder} {File}", folder, filepath);
            }

            return file;
        }
    }
}
