using FruitBank.Common.Entities;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Nop.Core;
using Nop.Plugin.Misc.FruitBankPlugin.Domains.DataLayer;
using Nop.Plugin.Misc.FruitBankPlugin.Services.FileStorage;
using Nop.Services.Security;
using Nop.Web.Framework;
using Nop.Web.Framework.Controllers;
using Nop.Web.Framework.Mvc.Filters;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;

namespace Nop.Plugin.Misc.FruitBank.Controllers
{
    [AuthorizeAdmin]
    [Area(AreaNames.ADMIN)]
    [AutoValidateAntiforgeryToken]
    public class FileStorageController : BasePluginController
    {
        private readonly FileStorageService _fileStorageService;
        private readonly FruitBankDbContext _dbContext;
        private readonly IPermissionService _permissionService;
        private readonly IWorkContext _workContext;

        public FileStorageController(
            FileStorageService fileStorageService,
            FruitBankDbContext dbContext,
            IPermissionService permissionService,
            IWorkContext workContext)
        {
            _fileStorageService = fileStorageService;
            _dbContext = dbContext;
            _permissionService = permissionService;
            _workContext = workContext;
        }

        #region Upload Files

        /// <summary>
        /// Upload a single file
        /// </summary>
        /// <param name="file">The uploaded file</param>
        /// <param name="featureName">Feature name (e.g., "AIdocumentprocessing")</param>
        /// <param name="entityType">Entity type (e.g., "ShippingDocuments")</param>
        /// <param name="entityId">Entity ID</param>
        /// <param name="rawText">Optional raw text for searchable documents</param>
        [HttpPost]
        public async Task<IActionResult> UploadFile(
            IFormFile file,
            string featureName,
            string entityType,
            int entityId,
            string rawText = null)
        {
            if (!await _permissionService.AuthorizeAsync(StandardPermission.Security.ACCESS_ADMIN_PANEL))
                return Json(new { success = false, message = "Access denied" });

            if (file == null || file.Length == 0)
                return Json(new { success = false, message = "No file uploaded" });

            if (string.IsNullOrWhiteSpace(featureName))
                return Json(new { success = false, message = "Feature name is required" });

            if (string.IsNullOrWhiteSpace(entityType))
                return Json(new { success = false, message = "Entity type is required" });

            if (entityId <= 0)
                return Json(new { success = false, message = "Valid entity ID is required" });

            try
            {
                var currentUser = await _workContext.GetCurrentCustomerAsync();
                var userId = currentUser.Id;

                using (var stream = file.OpenReadStream())
                {
                    var fileEntity = await _fileStorageService.SaveFileAsync(
                        fileStream: stream,
                        fileName: file.FileName,
                        userId: userId,
                        featureName: featureName,
                        entityType: entityType,
                        entityId: entityId,
                        rawText: rawText
                    );

                    return Json(new
                    {
                        success = true,
                        message = "File uploaded successfully",
                        file = new
                        {
                            id = fileEntity.Id,
                            fileName = fileEntity.FileName,
                            fileExtension = fileEntity.FileExtension,
                            created = fileEntity.Created,
                            hasRawText = !string.IsNullOrEmpty(fileEntity.RawText)
                        }
                    });
                }
            }
            catch (Exception ex)
            {
                Console.Error.WriteLine($"Error uploading file: {ex}");
                return Json(new
                {
                    success = false,
                    message = $"Error uploading file: {ex.Message}"
                });
            }
        }

        /// <summary>
        /// Upload multiple files at once
        /// </summary>
        [HttpPost]
        public async Task<IActionResult> UploadMultipleFiles(
            List<IFormFile> files,
            string featureName,
            string entityType,
            int entityId)
        {
            if (!await _permissionService.AuthorizeAsync(StandardPermission.Security.ACCESS_ADMIN_PANEL))
                return Json(new { success = false, message = "Access denied" });

            if (files == null || files.Count == 0)
                return Json(new { success = false, message = "No files uploaded" });

            try
            {
                var currentUser = await _workContext.GetCurrentCustomerAsync();
                var userId = currentUser.Id;
                var uploadedFiles = new List<object>();
                var errors = new List<string>();

                foreach (var file in files)
                {
                    try
                    {
                        if (file.Length > 0)
                        {
                            using (var stream = file.OpenReadStream())
                            {
                                var fileEntity = await _fileStorageService.SaveFileAsync(
                                    fileStream: stream,
                                    fileName: file.FileName,
                                    userId: userId,
                                    featureName: featureName,
                                    entityType: entityType,
                                    entityId: entityId
                                );

                                uploadedFiles.Add(new
                                {
                                    id = fileEntity.Id,
                                    fileName = fileEntity.FileName + fileEntity.FileExtension
                                });
                            }
                        }
                    }
                    catch (Exception ex)
                    {
                        errors.Add($"{file.FileName}: {ex.Message}");
                    }
                }

                return Json(new
                {
                    success = uploadedFiles.Count > 0,
                    message = $"Uploaded {uploadedFiles.Count} of {files.Count} files",
                    files = uploadedFiles,
                    errors = errors
                });
            }
            catch (Exception ex)
            {
                Console.Error.WriteLine($"Error uploading multiple files: {ex}");
                return Json(new
                {
                    success = false,
                    message = $"Error: {ex.Message}"
                });
            }
        }

        #endregion

        #region Download Files

        /// <summary>
        /// Download a file by ID
        /// </summary>
        [HttpGet]
        public async Task<IActionResult> DownloadFile(
            int fileId,
            string featureName,
            string entityType,
            int entityId)
        {
            if (!await _permissionService.AuthorizeAsync(StandardPermission.Security.ACCESS_ADMIN_PANEL))
                return Unauthorized();

            try
            {
                var currentUser = await _workContext.GetCurrentCustomerAsync();
                var userId = currentUser.Id;

                var (fileStream, fileInfo) = await _fileStorageService.GetFileByIdAsync(
                    fileId: fileId,
                    userId: userId,
                    featureName: featureName,
                    entityType: entityType,
                    entityId: entityId
                );

                var fileName = $"{fileInfo.FileName}{fileInfo.FileExtension}";
                var contentType = GetContentType(fileInfo.FileExtension);

                return File(fileStream, contentType, fileName);
            }
            catch (FileNotFoundException ex)
            {
                return NotFound(new { success = false, message = ex.Message });
            }
            catch (Exception ex)
            {
                Console.Error.WriteLine($"Error downloading file: {ex}");
                return BadRequest(new { success = false, message = ex.Message });
            }
        }

        /// <summary>
        /// Preview a file inline (for PDFs, images)
        /// </summary>
        [HttpGet]
        public async Task<IActionResult> PreviewFile(
            int fileId,
            string featureName,
            string entityType,
            int entityId)
        {
            if (!await _permissionService.AuthorizeAsync(StandardPermission.Security.ACCESS_ADMIN_PANEL))
                return Unauthorized();

            try
            {
                var currentUser = await _workContext.GetCurrentCustomerAsync();
                var userId = currentUser.Id;

                var (fileStream, fileInfo) = await _fileStorageService.GetFileByIdAsync(
                    fileId: fileId,
                    userId: userId,
                    featureName: featureName,
                    entityType: entityType,
                    entityId: entityId
                );

                var contentType = GetContentType(fileInfo.FileExtension);

                // Return inline for preview
                return File(fileStream, contentType);
            }
            catch (FileNotFoundException ex)
            {
                return NotFound(new { success = false, message = ex.Message });
            }
            catch (Exception ex)
            {
                Console.Error.WriteLine($"Error previewing file: {ex}");
                return BadRequest(new { success = false, message = ex.Message });
            }
        }

        #endregion

        #region List & Search Files

        /// <summary>
        /// Get all files
        /// </summary>
        [HttpGet]
        public async Task<IActionResult> GetAllFiles()
        {
            if (!await _permissionService.AuthorizeAsync(StandardPermission.Security.ACCESS_ADMIN_PANEL))
                return Json(new { success = false, message = "Access denied" });

            try
            {
                var files = await _fileStorageService.GetAllFilesAsync();

                return Json(new
                {
                    success = true,
                    count = files.Count,
                    files = files.Select(f => new
                    {
                        id = f.Id,
                        fileName = f.FileName,
                        fileExtension = f.FileExtension,
                        fullName = $"{f.FileName}{f.FileExtension}",
                        created = f.Created,
                        modified = f.Modified,
                        hasRawText = !string.IsNullOrEmpty(f.RawText)
                    })
                });
            }
            catch (Exception ex)
            {
                Console.Error.WriteLine($"Error getting files: {ex}");
                return Json(new { success = false, message = ex.Message });
            }
        }

        /// <summary>
        /// Search files by filename or content
        /// </summary>
        [HttpGet]
        public async Task<IActionResult> SearchFiles(string searchTerm)
        {
            if (!await _permissionService.AuthorizeAsync(StandardPermission.Security.ACCESS_ADMIN_PANEL))
                return Json(new { success = false, message = "Access denied" });

            if (string.IsNullOrWhiteSpace(searchTerm))
                return Json(new { success = false, message = "Search term is required" });

            try
            {
                var files = await _fileStorageService.SearchFilesAsync(searchTerm);

                return Json(new
                {
                    success = true,
                    searchTerm = searchTerm,
                    count = files.Count,
                    files = files.Select(f => new
                    {
                        id = f.Id,
                        fileName = f.FileName,
                        fileExtension = f.FileExtension,
                        fullName = $"{f.FileName}{f.FileExtension}",
                        created = f.Created,
                        hasRawText = !string.IsNullOrEmpty(f.RawText),
                        // Include snippet of matched text if available
                        textSnippet = GetTextSnippet(f.RawText, searchTerm)
                    })
                });
            }
            catch (Exception ex)
            {
                Console.Error.WriteLine($"Error searching files: {ex}");
                return Json(new { success = false, message = ex.Message });
            }
        }

        /// <summary>
        /// Get files for a specific entity
        /// </summary>
        [HttpGet]
        public async Task<IActionResult> GetEntityFiles(string entityType, int entityId)
        {
            if (!await _permissionService.AuthorizeAsync(StandardPermission.Security.ACCESS_ADMIN_PANEL))
                return Json(new { success = false, message = "Access denied" });

            try
            {
                // Example for ShippingDocuments - adapt for other entity types
                if (entityType == "ShippingDocuments")
                {
                    var mappings = await _dbContext.ShippingDocumentToFiles
                        .GetAll()
                        .Where(m => m.ShippingDocumentId == entityId)
                        .ToListAsync();

                    var fileIds = mappings.Select(m => m.FilesId).ToList();
                    var files = await _dbContext.Files
                        .GetAll()
                        .Where(f => fileIds.Contains(f.Id))
                        .ToListAsync();

                    return Json(new
                    {
                        success = true,
                        entityType = entityType,
                        entityId = entityId,
                        count = files.Count,
                        files = files.Select(f => new
                        {
                            id = f.Id,
                            fileName = $"{f.FileName}{f.FileExtension}",
                            created = f.Created,
                            documentType = mappings.FirstOrDefault(m => m.FilesId == f.Id)?.DocumentType.ToString()
                        })
                    });
                }

                return Json(new
                {
                    success = false,
                    message = $"Entity type '{entityType}' not supported yet"
                });
            }
            catch (Exception ex)
            {
                Console.Error.WriteLine($"Error getting entity files: {ex}");
                return Json(new { success = false, message = ex.Message });
            }
        }

        #endregion

        #region Delete Files

        /// <summary>
        /// Delete a file by ID
        /// </summary>
        [HttpPost]
        public async Task<IActionResult> DeleteFile(
            int fileId,
            string featureName,
            string entityType,
            int entityId)
        {
            if (!await _permissionService.AuthorizeAsync(StandardPermission.Security.ACCESS_ADMIN_PANEL))
                return Json(new { success = false, message = "Access denied" });

            try
            {
                var currentUser = await _workContext.GetCurrentCustomerAsync();
                var userId = currentUser.Id;

                var deleted = await _fileStorageService.DeleteFileAsync(
                    fileId: fileId,
                    userId: userId,
                    featureName: featureName,
                    entityType: entityType,
                    entityId: entityId
                );

                if (deleted)
                {
                    // Also delete mappings (example for ShippingDocuments)
                    if (entityType == "ShippingDocuments")
                    {
                        var mappings = await _dbContext.ShippingDocumentToFiles
                            .GetAll()
                            .Where(m => m.FilesId == fileId && m.ShippingDocumentId == entityId)
                            .ToListAsync();

                        foreach (var mapping in mappings)
                        {
                            await _dbContext.ShippingDocumentToFiles.DeleteAsync(mapping);
                        }

                        
                    }
                }

                return Json(new
                {
                    success = deleted,
                    message = deleted ? "File deleted successfully" : "File not found"
                });
            }
            catch (Exception ex)
            {
                Console.Error.WriteLine($"Error deleting file: {ex}");
                return Json(new
                {
                    success = false,
                    message = $"Error deleting file: {ex.Message}"
                });
            }
        }

        /// <summary>
        /// Delete multiple files at once
        /// </summary>
        [HttpPost]
        public async Task<IActionResult> DeleteMultipleFiles(
            List<int> fileIds,
            string featureName,
            string entityType,
            int entityId)
        {
            if (!await _permissionService.AuthorizeAsync(StandardPermission.Security.ACCESS_ADMIN_PANEL))
                return Json(new { success = false, message = "Access denied" });

            if (fileIds == null || fileIds.Count == 0)
                return Json(new { success = false, message = "No file IDs provided" });

            try
            {
                var currentUser = await _workContext.GetCurrentCustomerAsync();
                var userId = currentUser.Id;

                var deletedCount = 0;
                var errors = new List<string>();

                foreach (var fileId in fileIds)
                {
                    try
                    {
                        var deleted = await _fileStorageService.DeleteFileAsync(
                            fileId: fileId,
                            userId: userId,
                            featureName: featureName,
                            entityType: entityType,
                            entityId: entityId
                        );

                        if (deleted)
                        {
                            deletedCount++;
                        }
                    }
                    catch (Exception ex)
                    {
                        errors.Add($"File {fileId}: {ex.Message}");
                    }
                }

                return Json(new
                {
                    success = deletedCount > 0,
                    message = $"Deleted {deletedCount} of {fileIds.Count} files",
                    deletedCount = deletedCount,
                    errors = errors
                });
            }
            catch (Exception ex)
            {
                Console.Error.WriteLine($"Error deleting multiple files: {ex}");
                return Json(new
                {
                    success = false,
                    message = $"Error: {ex.Message}"
                });
            }
        }

        #endregion

        #region File Information

        /// <summary>
        /// Get file information by ID
        /// </summary>
        [HttpGet]
        public async Task<IActionResult> GetFileInfo(int fileId)
        {
            if (!await _permissionService.AuthorizeAsync(StandardPermission.Security.ACCESS_ADMIN_PANEL))
                return Json(new { success = false, message = "Access denied" });

            try
            {
                var fileEntity = await _dbContext.Files.GetByIdAsync(fileId);

                if (fileEntity == null)
                    return NotFound(new { success = false, message = "File not found" });

                return Json(new
                {
                    success = true,
                    file = new
                    {
                        id = fileEntity.Id,
                        fileName = fileEntity.FileName,
                        fileExtension = fileEntity.FileExtension,
                        fullName = $"{fileEntity.FileName}{fileEntity.FileExtension}",
                        created = fileEntity.Created,
                        modified = fileEntity.Modified,
                        hasRawText = !string.IsNullOrEmpty(fileEntity.RawText),
                        rawTextLength = fileEntity.RawText?.Length ?? 0
                    }
                });
            }
            catch (Exception ex)
            {
                Console.Error.WriteLine($"Error getting file info: {ex}");
                return Json(new { success = false, message = ex.Message });
            }
        }

        /// <summary>
        /// Update file metadata (RawText)
        /// </summary>
        [HttpPost]
        public async Task<IActionResult> UpdateFileMetadata(int fileId, string rawText)
        {
            if (!await _permissionService.AuthorizeAsync(StandardPermission.Security.ACCESS_ADMIN_PANEL))
                return Json(new { success = false, message = "Access denied" });

            try
            {
                var fileEntity = await _dbContext.Files.GetByIdAsync(fileId);

                if (fileEntity == null)
                    return NotFound(new { success = false, message = "File not found" });

                fileEntity.RawText = rawText;
                await _fileStorageService.AddOrUpdateFileAsync(fileEntity);

                return Json(new
                {
                    success = true,
                    message = "File metadata updated successfully"
                });
            }
            catch (Exception ex)
            {
                Console.Error.WriteLine($"Error updating file metadata: {ex}");
                return Json(new
                {
                    success = false,
                    message = $"Error updating metadata: {ex.Message}"
                });
            }
        }

        #endregion

        #region Helper Methods

        /// <summary>
        /// Get content type based on file extension
        /// </summary>
        private string GetContentType(string extension)
        {
            return extension.ToLowerInvariant() switch
            {
                ".pdf" => "application/pdf",
                ".jpg" or ".jpeg" => "image/jpeg",
                ".png" => "image/png",
                ".gif" => "image/gif",
                ".webp" => "image/webp",
                ".bmp" => "image/bmp",
                ".txt" => "text/plain",
                ".csv" => "text/csv",
                ".json" => "application/json",
                ".xml" => "application/xml",
                ".html" => "text/html",
                ".doc" => "application/msword",
                ".docx" => "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
                ".xls" => "application/vnd.ms-excel",
                ".xlsx" => "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
                ".zip" => "application/zip",
                ".rar" => "application/x-rar-compressed",
                _ => "application/octet-stream"
            };
        }

        /// <summary>
        /// Get a snippet of text around the search term
        /// </summary>
        private string GetTextSnippet(string text, string searchTerm, int contextLength = 100)
        {
            if (string.IsNullOrEmpty(text) || string.IsNullOrEmpty(searchTerm))
                return null;

            var index = text.IndexOf(searchTerm, StringComparison.OrdinalIgnoreCase);
            if (index == -1)
                return null;

            var start = Math.Max(0, index - contextLength);
            var length = Math.Min(text.Length - start, contextLength * 2 + searchTerm.Length);

            var snippet = text.Substring(start, length);

            if (start > 0)
                snippet = "..." + snippet;

            if (start + length < text.Length)
                snippet = snippet + "...";

            return snippet;
        }

        #endregion
    }
}