using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Caching.Memory;
using Syncfusion.EJ2.PdfViewer;
using System.Text.Json;
using Microsoft.AspNetCore.Cors;

namespace IWS5.Api.Controllers
{
    /// <summary>
    /// Controller for handling PDF document.
    /// </summary>
    [ApiController]
    [Route("api/[controller]")]
    [DisableRequestSizeLimit]
    public class PdfViewerController : ControllerBase
    {
        private readonly PdfRenderer _pdfRenderer;
        public PdfViewerController(IMemoryCache memoryCache)
        {
            _pdfRenderer = new PdfRenderer(memoryCache, 20);
        }

        /// <summary>
        /// Loads a PDF document into the viewer.
        /// </summary>
        /// <param name="args">A dictionary containing parameters for loading the PDF document.</param>
        /// <returns>
        /// A JSON-serialized object containing the loaded PDF document.
        /// </returns>
        /// <response code="200">Returns the loaded PDF document successfully.</response>
        /// <response code="400">If the request data is invalid or the document is missing.</response>
        /// <response code="500">If an internal server error occurs.</response>
        [AcceptVerbs("Post")]
        [HttpPost]
        [Route("Load")]

        public IActionResult Load([FromBody] Dictionary<string, object> args)
        {
            Dictionary<string, string> jsonObject = ConvertArgs(args);
            MemoryStream stream = new MemoryStream();
            object jsonResult = new object();
            if (jsonObject != null && jsonObject.ContainsKey("document"))
            {
                string documentName = jsonObject["document"];
                byte[] bytes = Convert.FromBase64String(documentName);
                stream = new MemoryStream(bytes);
            }
            jsonResult = _pdfRenderer.Load(stream, jsonObject);
            
            return Ok(JsonSerializer.Serialize(jsonResult));
        }

        /// <summary>
        /// Retrieves bookmarks from a PDF document.
        /// </summary>
        /// <param name="args">A dictionary containing parameters for extracting bookmarks.</param>
        /// <returns>
        /// A JSON-serialized object containing the extracted bookmarks.
        /// </returns>
        /// <response code="200">Returns the extracted bookmarks successfully.</response>
        /// <response code="400">If the request data is invalid.</response>
        /// <response code="500">If an internal server error occurs.</response>
        [AcceptVerbs("Post")]
        [HttpPost]
        [Route("Bookmarks")]
        public IActionResult Bookmarks([FromBody] Dictionary<string, object> args)
        {
            object jsonResult = _pdfRenderer.GetBookmarks(ConvertArgs(args));
            return Content(JsonSerializer.Serialize(jsonResult));
        }

        /// <summary>
        /// Renders pages from a PDF document.
        /// </summary>
        /// <param name="args">A dictionary containing parameters for rendering PDF pages.</param>
        /// <returns>
        /// A JSON-serialized object containing the rendered PDF pages.
        /// </returns>
        /// <response code="200">Returns the rendered PDF pages successfully.</response>
        /// <response code="400">If the request data is invalid.</response>
        /// <response code="500">If an internal server error occurs.</response>
        [AcceptVerbs("Post")]
        [HttpPost]
        [Route("RenderPdfPages")]
        public IActionResult RenderPdfPages([FromBody] Dictionary<string, object> args)
        {
            object jsonResult = _pdfRenderer.GetPage(ConvertArgs(args));
            return Content(JsonSerializer.Serialize(jsonResult));
        }

        /// <summary>
        /// Extracts text content from a PDF document.
        /// </summary>
        /// <param name="args">A dictionary containing parameters for extracting text from the PDF.</param>
        /// <returns>
        /// A JSON-serialized object containing the extracted text content.
        /// </returns>
        /// <response code="200">Returns the extracted text successfully.</response>
        /// <response code="400">If the request data is invalid.</response>
        /// <response code="500">If an internal server error occurs.</response>
        [AcceptVerbs("Post")]
        [HttpPost]
        [Route("RenderPdfTexts")]
        //Post action for processing the PDF texts  
        public IActionResult RenderPdfTexts([FromBody] Dictionary<string, object> args)
        {
            object jsonResult = _pdfRenderer.GetDocumentText(ConvertArgs(args));
            return Content(JsonSerializer.Serialize(jsonResult));
        }

        /// <summary>
        /// Retrieves annotation comments from a PDF document.
        /// </summary>
        /// <param name="args">A dictionary containing parameters for extracting annotation comments.</param>
        /// <returns>
        /// A JSON-serialized object containing the annotation comments.
        /// </returns>
        /// <response code="200">Returns the annotation comments successfully.</response>
        /// <response code="400">If the request data is invalid.</response>
        /// <response code="500">If an internal server error occurs.</response>
        [AcceptVerbs("Post")]
        [HttpPost]
        [Route("RenderAnnotationComments")]
        public IActionResult RenderAnnotationComments([FromBody] Dictionary<string, object> args)
        {
            object jsonResult = _pdfRenderer.GetAnnotationComments(ConvertArgs(args));
            return Content(JsonSerializer.Serialize(jsonResult));
        }

        /// <summary>
        /// Clears the cached data for a PDF document.
        /// </summary>
        /// <param name="args">A dictionary containing parameters for clearing the document cache.</param>
        /// <returns>
        /// A string message indicating that the document cache has been cleared.
        /// </returns>
        /// <response code="200">Indicates that the document cache has been successfully cleared.</response>
        /// <response code="400">If the request data is invalid.</response>
        /// <response code="500">If an internal server error occurs.</response>
        [AcceptVerbs("Post")]
        [HttpPost]
        [Route("Unload")]
        public IActionResult Unload([FromBody] Dictionary<string, object> args)
        {
            _pdfRenderer.ClearCache(ConvertArgs(args));
            return this.Content("Document cache is cleared");
        }

        /// <summary>
        /// Generates and returns thumbnail images for a PDF document.
        /// </summary>
        /// <param name="args">A dictionary containing parameters for rendering thumbnail images.</param>
        /// <returns>
        /// A JSON-serialized object containing the generated thumbnail images.
        /// </returns>
        /// <response code="200">Returns the generated thumbnail images.</response>
        /// <response code="400">If the request data is invalid.</response>
        /// <response code="500">If an internal server error occurs.</response>
        [AcceptVerbs("Post")]
        [HttpPost]
        [Route("RenderThumbnailImages")]
        public IActionResult RenderThumbnailImages([FromBody] Dictionary<string, object> args)
        {
            object result = _pdfRenderer.GetThumbnailImages(ConvertArgs(args));
            return Content(JsonSerializer.Serialize(result));
        }

        /// <summary>
        /// Downloads a PDF document as a Base64-encoded string.
        /// </summary>
        /// <param name="args">A dictionary containing parameters for retrieving the PDF document.</param>
        /// <returns>
        /// A Base64-encoded string representing the downloaded PDF document.
        /// </returns>
        /// <response code="200">Returns the downloaded PDF document as a Base64-encoded string.</response>
        /// <response code="400">If the request data is invalid.</response>
        /// <response code="500">If an internal server error occurs.</response>
        [HttpPost]
        [Route("Download")]
        public IActionResult Download([FromBody] Dictionary<string, object> args)
        {
            string documentBase = _pdfRenderer.GetDocumentAsBase64(ConvertArgs(args));
            return Content(documentBase);
        }

        /// <summary>
        /// Saves a PDF document to a specified URL.
        /// </summary>
        /// <param name="args">A dictionary containing the document data and target URL.</param>
        /// <returns>
        /// A response indicating whether the document was successfully saved.
        /// </returns>
        /// <response code="200">Indicates the document was saved successfully.</response>
        /// <response code="400">If the request data is invalid.</response>
        /// <response code="500">If an internal server error occurs.</response>
        [HttpPost]
        [Route("SaveUrl")]
        public async Task<IActionResult> SaveUrl([FromBody] Dictionary<string, object> args)
        {
            Dictionary<string, string> jsonObject = ConvertArgs(args);
            try
            {
                HttpClient client = new HttpClient();
                Dictionary<string, string> RequestDetails = new Dictionary<string, string>();
                RequestDetails.Add("Url", jsonObject["RequestedUrl"]);
                RequestDetails.Add("base64", jsonObject["base64Data"]);
                string jsonString = JsonSerializer.Serialize(RequestDetails);
                StringContent Data = new StringContent(jsonString);
                string UriAddress = jsonObject["UriAddress"];
                var result = await client.PutAsync(UriAddress, Data);
                if (result.StatusCode.ToString() == "OK")
                {
                    return Content("Document saved successfully!");
                }
                else
                {
                    return Content("Failed to save the document!");
                }
            }
            catch (Exception exception)
            {
                return Content(exception.Message);
            }
        }

        /// <summary>
        /// Generates printable images from a PDF document.
        /// </summary>
        /// <param name="args">A dictionary containing parameters for generating printable images.</param>
        /// <returns>
        /// A JSON-serialized object containing the printable images.
        /// </returns>
        /// <response code="200">Returns the generated printable images.</response>
        /// <response code="400">If the request data is invalid.</response>
        /// <response code="500">If an internal server error occurs.</response>
        [AcceptVerbs("Post")]
        [HttpPost]
        [Route("PrintImages")]
        public IActionResult PrintImages([FromBody] Dictionary<string, object> args)
        {
            object pageImage = _pdfRenderer.GetPrintImage(ConvertArgs(args));
            return Content(JsonSerializer.Serialize(pageImage));
        }

        /// <summary>
        /// Exports annotations from a PDF document.
        /// </summary>
        /// <param name="args">A dictionary containing the parameters for exporting annotations.</param>
        /// <returns>
        /// A JSON-serialized string representing the exported annotations.
        /// </returns>
        /// <response code="200">Returns the exported annotations.</response>
        /// <response code="400">If the request data is invalid.</response>
        /// <response code="500">If an internal server error occurs.</response>
        [AcceptVerbs("Post")]
        [HttpPost]
        [Route("ExportAnnotations")]
        public IActionResult ExportAnnotations([FromBody] Dictionary<string, object> args)
        {
            string jsonResult = _pdfRenderer.ExportAnnotation(ConvertArgs(args));
            return Content(jsonResult);
        }

        /// <summary>
        /// Imports annotations into a PDF document.
        /// </summary>
        /// <param name="args">A dictionary containing the annotation data to be imported.</param>
        /// <returns>
        /// A JSON-serialized response containing the result of the annotation import operation.
        /// </returns>
        /// <response code="200">Returns the imported annotations.</response>
        /// <response code="400">If the request data is invalid or missing required fields.</response>
        /// <response code="404">If the specified document is not found.</response>
        /// <response code="500">If an internal server error occurs.</response>
        [AcceptVerbs("Post")]
        [HttpPost]
        [Route("ImportAnnotations")]
        public IActionResult ImportAnnotations([FromBody] Dictionary<string, object> args)
        {
            Dictionary<string, string> jsonObject = ConvertArgs(args);
            string jsonResult = string.Empty;
            object JsonResult;
            if (jsonObject != null && jsonObject.ContainsKey("fileName"))
            {
                string documentPath = GetDocumentPath(jsonObject["fileName"]);
                if (!string.IsNullOrEmpty(documentPath))
                {
                    jsonResult = System.IO.File.ReadAllText(documentPath);
                }
                else
                {
                    return this.Content(jsonObject["document"] + " is not found");
                }
            }
            else
            {
                string extension = Path.GetExtension(jsonObject["importedData"]);
                if (extension != ".xfdf")
                {
                    JsonResult = _pdfRenderer.ImportAnnotation(jsonObject);
                    return Content(JsonSerializer.Serialize(JsonResult));
                }
                else
                {
                    string documentPath = GetDocumentPath(jsonObject["importedData"]);
                    if (!string.IsNullOrEmpty(documentPath))
                    {
                        byte[] bytes = System.IO.File.ReadAllBytes(documentPath);
                        jsonObject["importedData"] = Convert.ToBase64String(bytes);
                        JsonResult = _pdfRenderer.ImportAnnotation(jsonObject);
                        return Content(JsonSerializer.Serialize(JsonResult));
                    }
                    else
                    {
                        return this.Content(jsonObject["document"] + " is not found");
                    }
                }

            }
            return Content(jsonResult);
        }

        /// <summary>
        /// Exports form fields from a PDF document.
        /// </summary>
        /// <param name="args">A dictionary containing the parameters for exporting form fields.</param>
        /// <returns>
        /// A JSON-serialized string representing the exported form fields.
        /// </returns>
        /// <response code="200">Returns the exported form fields.</response>
        /// <response code="400">If the request data is invalid.</response>
        /// <response code="500">If an internal server error occurs.</response>
        [AcceptVerbs("Post")]
        [HttpPost]
        [Route("ExportFormFields")]
        public IActionResult ExportFormFields([FromBody] Dictionary<string, object> args)
        {
            string jsonResult = _pdfRenderer.ExportFormFields(ConvertArgs(args));
            return Content(jsonResult);
        }

        /// <summary>
        /// Imports form fields into a PDF document.
        /// </summary>
        /// <param name="args">A dictionary containing the form field data to be imported.</param>
        /// <returns>
        /// A JSON-serialized response containing the result of the form field import operation.
        /// </returns>
        /// <response code="200">Returns the imported form fields.</response>
        /// <response code="400">If the request data is invalid.</response>
        /// <response code="500">If an internal server error occurs.</response>
        [AcceptVerbs("Post")]
        [HttpPost]
        [Route("ImportFormFields")]
        public IActionResult ImportFormFields([FromBody] Dictionary<string, object> args)
        {
            object jsonResult = _pdfRenderer.ImportFormFields(ConvertArgs(args));
            return Content(JsonSerializer.Serialize(jsonResult));
        }

        private string GetDocumentPath(string document)
        {
            if (!System.IO.File.Exists(document))
            {
                return string.Empty;
            }
            else
                return document;
        }

        private Dictionary<string, string> ConvertArgs(Dictionary<string, object> args)
        {
            return args?.ToDictionary(k => k.Key, k => k.Value?.ToString()) ?? new Dictionary<string, string>();
        }
    }
}
