using IWS5.Api.OptionsSetup;
using IWS5.Core.DTO;
using IWS5.Core.Enums;
using IWS5.Core.Exceptions;
using IWS5.Core.Models;
using IWS5.Core.Models.Request;
using IWS5.Core.Services;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Swashbuckle.AspNetCore.Annotations;

namespace IWS5.Api.Controllers;

/// <summary>
/// Controller for managing documents.
/// </summary>
/// <param name="documentService">Service for handling document-related operations.</param>
/// <param name="logger">Logger for logging information and errors.</param>
[Authorize]
[Route("api/[controller]")]
[ApiController]
public class DocumentController(IDocumentService documentService, ILogger<DocumentController> logger)
    : ControllerBase
{
   
    /// <summary>
    /// Retrieves a list of document fields based on the specified work item reference ID.
    /// </summary>
    /// <param name="workItemReferenceId">The reference ID of the work item.</param>
    /// <param name="includingDocumentContent">Indicates whether to include the document content.</param>
    /// <returns>A list of document fields.</returns>
    /// <response code="200">Returns the list of document fields.</response>
    /// <response code="400">If the request is invalid.</response>
    /// <response code="401">If the user is not authenticated.</response>
    /// <response code="409">If document fields are locked for the user.</response>
    [HttpGet("list")]
    [SwaggerOperation(Summary = "Retrieves a list of document fields based on the specified work item reference ID.")]
    [ProducesResponseType(typeof(Response<List<DocumentListItemDTO>>), 200)]
    [ProducesResponseType(400)]
    [ProducesResponseType(401)]
    [ProducesResponseType(409)]
    public async Task<IActionResult> ListDocumentFields([FromQuery] int workItemReferenceId,
        [FromQuery] bool includingDocumentContent)
    {
        try
        {
            var userName = JwtTokenHelper.GetUserName(HttpContext, logger, out var actionResult);

            if (string.IsNullOrEmpty(userName))
            {
                return actionResult;
            }

            var result =
                await documentService.ListDocumentFields(new DocumentsRequest(userName, workItemReferenceId, includingDocumentContent));
            return Ok(result);
        }
        catch (NotFoundException ex)
        {
            return BadRequest(ex.Message);
        }
        catch (InvalidOperationException ex)
        {
            return Conflict(new { error = ex.Message });
        }
        catch (Exception ex)
        {
            logger.LogError(ex, "An error occurred while retrieving document fields for WorkItemReferenceId {WorkItemReferenceId}", workItemReferenceId);
            return BadRequest("An error occurred while retrieving document fields.");
        }
    }

   
    /// <summary>
    /// Retrieves the content of a specific document.
    /// </summary>
    /// <param name="documentId">The ID of the document.</param>
    /// <returns>The content of the document.</returns>
    /// <response code="200">Returns the document content.</response>
    /// <response code="404">If the document is not found.</response>
    /// <response code="409">If document content is locked for the user.</response>
    [HttpGet("{documentId:int}")]
    [SwaggerOperation(Summary = "Retrieves the content of a specific document.")]
    [ProducesResponseType(typeof(DocumentDTO), 200)]
    [ProducesResponseType(404)]
    [ProducesResponseType(409)]
    public async Task<IActionResult> GetDocumentContent([FromRoute] int documentId)
    {
        try
        {
            var userName = JwtTokenHelper.GetUserName(HttpContext, logger, out var actionResult);

            if (string.IsNullOrEmpty(userName))
            {
                return actionResult;
            }

            var result = await documentService.GetDocument(new DocumentRequest(documentId, userName));
            return Ok(result);
        }
        catch (InvalidOperationException ex)
        {
            return Conflict(new { error = ex.Message });
        }
        catch (Exception ex)
        {
            logger.LogError(ex, "An error occurred while retrieving document content for DocumentId {DocumentId}", documentId);
            return BadRequest("An error occurred while retrieving document content.");
        }
    }

    /// <summary>
    /// Renames a specific document.
    /// </summary>
    /// <param name="request">The request containing the new document name (without the file extension).</param>
    /// <returns>An action result indicating the outcome of the operation.</returns>
    /// <response code="200">If the document was successfully renamed.</response>
    /// <response code="400">If the request is invalid.</response>
    /// <response code="401">If the user is not authenticated.</response>
    /// <response code="403">If the user lacks necessary rights.</response>
    /// <response code="409">If document editing is locked for the user.</response>
    [HttpPatch("rename")]
    [SwaggerOperation(Summary = "Renames a specific document.")]
    [ProducesResponseType(200)]
    [ProducesResponseType(400)]
    [ProducesResponseType(401)]
    [ProducesResponseType(403)]
    [ProducesResponseType(409)]
    public async Task<IActionResult> UpdateDocumentName([FromBody] RenameDocumentRequest request) {
        var userName = JwtTokenHelper.GetUserName(HttpContext, logger, out var actionResult);

        if (string.IsNullOrEmpty(userName)) {
            return actionResult;
        }

        var result = await documentService.RenameDocument(userName, request);

        if (result.Code != ResponseCode.OK) {
            return BadRequest($"Document {request.DocumentId} could not be updated.");
        }

        return Ok(result);
    }

    /// <summary>
    /// Updates the content of a specific document.
    /// </summary>
    /// <param name="request">The request containing the document content to update.</param>
    /// <returns>An action result indicating the outcome of the operation.</returns>
    /// <response code="200">If the document content was successfully updated.</response>
    /// <response code="400">If the request is invalid.</response>
    /// <response code="401">If the user is not authenticated.</response>
    /// <response code="403">If the user lacks necessary rights.</response>
    /// <response code="409">If document editing is locked for the user.</response>
    [HttpPut("update-doc-content")]
    [SwaggerOperation(Summary = "Updates the content of a specific document.")]
    [ProducesResponseType(200)]
    [ProducesResponseType(400)]
    [ProducesResponseType(401)]
    [ProducesResponseType(403)]
    [ProducesResponseType(409)]
    public async Task<IActionResult> UpdateDocumentContent([FromBody] UpdateDocumentContentRequest request) 
    {
        var userName = JwtTokenHelper.GetUserName(HttpContext, logger, out var actionResult);

        if (string.IsNullOrEmpty(userName)) {
            return actionResult;
        }

        var result = await documentService.UpdateDocumentContent(userName, request);

        if (result.Code != ResponseCode.OK) {
            return BadRequest($"Document {request.DocumentId} could not be updated.");
        }

        return Ok(result);
    }

    /// <summary>
    /// Updates the fields of a specific document.
    /// </summary>
    /// <param name="request">The request containing the document fields to update.</param>
    /// <returns>An action result indicating the outcome of the operation.</returns>
    /// <response code="200">If the document fields were successfully updated.</response>
    /// <response code="400">If the request is invalid.</response>
    /// <response code="401">If the user is not authenticated.</response>
    /// <response code="409">If document editing is locked for the user.</response>
    /// <response code="403">If the user lacks necessary rights.</response>
    [HttpPut("update-doc-fields")]
    [SwaggerOperation(Summary = "Updates the fields of a specific document.")]
    [ProducesResponseType(200)]
    [ProducesResponseType(400)]
    [ProducesResponseType(401)]
    [ProducesResponseType(403)]
    [ProducesResponseType(409)]
    public async Task<IActionResult> UpdateDocumentField([FromBody] UpdateDocumentFieldListRequest request)
    {
        try
        {
            var userName = JwtTokenHelper.GetUserName(HttpContext, logger, out var actionResult);

            if (string.IsNullOrEmpty(userName))
            {
                return actionResult;
            }

            await documentService.UpdateDocumentFields(userName, request);
            return Ok();
        }
        catch (InvalidOperationException ex)
        {
            return Conflict(new { error = ex.Message });
        }
        catch (Exception ex)
        {
            logger.LogError(ex, "An error occurred while updating document fields for DocumentId {DocumentId}", request.DocumentId);
            return BadRequest("An error occurred while updating document fields.");

        }
    }

    /// <summary>
    /// Deletes or marks a document as deleted based on the `permanently` flag.
    /// </summary>
    /// <param name="documentId">The ID of the document to delete.</param>
    /// <param name="permanently">Whether to permanently delete the document or mark it as deleted.</param>
    /// <returns>An action result indicating the outcome of the operation.</returns>
    /// <response code="200">If the document was successfully deleted or marked as deleted.</response>
    /// <response code="400">If the request is invalid.</response>
    /// <response code="403">If the user lacks necessary rights.</response>
    /// <response code="404">If the document is not found.</response>
    /// <response code="409">If document deleting is locked for the user.</response>
    [HttpDelete]
    [SwaggerOperation(Summary = "Deletes or marks a document as deleted based on the `permanently` flag.")]
    [ProducesResponseType(200)]
    [ProducesResponseType(400)]
    [ProducesResponseType(403)]
    [ProducesResponseType(404)]
    [ProducesResponseType(409)]
    public async Task<IActionResult> DeleteDocument([FromQuery] int documentId, [FromQuery] bool permanently)
    {
        try
        {
            if (documentId <= 0)
            {
                logger.LogWarning("Invalid documentId provided: {DocumentId}", documentId);
                return BadRequest("Document ID must be greater than 0.");
            }

            var userName = JwtTokenHelper.GetUserName(HttpContext, logger, out var actionResult);

            if (string.IsNullOrEmpty(userName))
            {
                return actionResult;
            }

            var result = await documentService.DeleteDocument(userName, documentId, permanently);

            if (!result.Success)
            {
                return NotFound($"Document with ID {documentId} not found.");
            }

            return Ok(result);
        }
        catch (ForbiddenAccessException ex)
        {
            throw;
        }
        catch (InvalidOperationException ex)
        {
            return Conflict(new { error = ex.Message });
        }
        catch (NotFoundException)
        {
            throw;
        }
        catch (Exception ex)
        {
            logger.LogError(ex, "An error occurred while deleting document with ID {DocumentId}", documentId);
            return BadRequest("An error occurred while deleting document.");
        }
    }

    /// <summary>
    /// Adds a new document to the system.
    /// </summary>
    /// <param name="documentRequest">The request to add a document.</param>
    /// <returns>An action result indicating the outcome of the operation.</returns>
    /// <response code="200">If the document was successfully added.</response>
    /// <response code="400">If the request is invalid.</response>
    /// <response code="403">If the user lacks necessary rights.</response>
    [HttpPost("add-document")]
    [SwaggerOperation(Summary = "Adds a new document to the system.")]
    [ProducesResponseType(200)]
    [ProducesResponseType(400)]
    [ProducesResponseType(403)]
    [DisableRequestSizeLimit]
    public async Task<IActionResult> AddDocument([FromBody] AddDocumentRequest documentRequest)
    {
        var userName = JwtTokenHelper.GetUserName(HttpContext, logger, out var actionResult);

        if (string.IsNullOrEmpty(userName))
        {
            return actionResult;
        }

        var result = await documentService.AddDocument(userName, documentRequest);

        if (!result.Success)
        {
            return BadRequest($"Document {documentRequest.DocumentName} could not be added.");
        }

        return Ok(result);
    }
}