using IWS5.Api.Enum;
using IWS5.Api.Helpers;
using IWS5.Api.OptionsSetup;
using IWS5.Core.DTO;
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 work items.
/// </summary>
[Authorize]
[Route("api/[controller]")]
[ApiController]
[SwaggerTag("Controller for managing work items.")]
public class WorkItemController(IWorkItemService workItemService, ILogger<WorkItemController> logger)
    : ControllerBase
{
    /// <summary>
    /// Retrieves work items from a specified queue.
    /// </summary>
    /// <param name="queueName">The ID of the queue to retrieve work items from.</param>
    /// <returns>A list of work items.</returns>
    /// <response code="200">Returns the list of work items.</response>
    /// <response code="401">If the user is unauthorized.</response>
    /// <response code="500">If an internal server error occurs.</response>
    [HttpGet]
    [SwaggerOperation(Summary = "Retrieves work items from a specified queue.")]
    [ProducesResponseType(typeof(CodeResponse<List<WorkItemListItemDTO>>), 200)]
    [ProducesResponseType(401)]
    [ProducesResponseType(500)]
    public async Task<IActionResult> GetWorkItems([FromQuery] int queueId)
    {
        var userName = JwtTokenHelper.GetUserName(HttpContext, logger, out var actionResult);

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

        var result = await workItemService.ListWorkItems(new WorkItemByQueueRequest(userName, queueId));
        var response = ResponseMapper<List<WorkItemListItemDTO>>.MapServiceResponse(result);
        logger.LogInformation("Successfully retrieved work items for user {UserName} from queue {QueueName}",
            userName, queueId);
        return response;
    }
    
    /// <summary>
    /// Retrieves work items for the current user's inbox.
    /// </summary>
    /// <returns>A list of work items in the user's inbox.</returns>
    /// <response code="200">Returns the list of work items in the user's inbox.</response>
    /// <response code="401">If the user is unauthorized.</response>
    /// <response code="500">If an internal server error occurs.</response>
    [HttpGet("inbox")]
    [SwaggerOperation(Summary = "Retrieves work items for the current user's inbox.")]
    [ProducesResponseType(typeof(CodeResponse<List<WorkItemInboxListItemDto>>), 200)]
    [ProducesResponseType(401)]
    [ProducesResponseType(500)]
    public async Task<IActionResult> GetWorkItemsForUser()
    {
        var userName = JwtTokenHelper.GetUserName(HttpContext, logger, out var actionResult);

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

        var result = await workItemService.ListWorkItems(new BaseRequest(userName));
        var response = ResponseMapper<List<WorkItemInboxListItemDto>>.MapServiceResponse(result);
        logger.LogInformation("Successfully retrieved work items for user {UserName} inbox", userName);
        return response;
    }

    /// <summary>
    /// Creates a new work item.
    /// </summary>
    /// <param name="createWorkItemRequest">The request to create work item.</param>
    /// <returns>An action result indicating the outcome of the operation.</returns>
    /// <response code="200">If the work item was successfully added.</response>
    /// <response code="400">If the request is invalid.</response>
    /// <response code="403">If the user lacks necessary rights.</response>
    [HttpPost("create")]
    [SwaggerOperation(Summary = "Creates work item.")]
    [ProducesResponseType(typeof(Response<List<WorkItemListItemDTO>>), 200)]
    [ProducesResponseType(401)]
    [ProducesResponseType(500)]
    public async Task<IActionResult> CreateWorkItem([FromBody] CreateWorkItemRequest createWorkItemRequest)
    {
        var userName = JwtTokenHelper.GetUserName(HttpContext, logger, out var actionResult);

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

        try
        {
            var devLog = await workItemService.CreateWorkItemAsync(createWorkItemRequest, userName);

            logger.LogInformation("Work item successfully created by user {UserName}.", userName);
            return Ok(devLog);
        }
        catch (Exception ex)
        {
            logger.LogError(ex, "An error occurred while creating work item for user {UserName}.", userName);
            return StatusCode(500, "An error occurred while creating the work item.");
        }
    }

    /// <summary>
    /// Clones a work item.
    /// </summary>
    /// <param name="cloneWorkItemRequest">Request model for cloning a work item.</param>
    /// <returns>An action result indicating the outcome of the operation.</returns>
    /// <response code="200">If the work item was successfully cloned.</response>
    /// <response code="400">If the request is invalid.</response>
    /// <response code="401">If the user is not authorized.</response>
    /// <response code="403">If the user lacks the necessary rights.</response>
    /// <response code="500">If an internal server error occurs.</response>
    [HttpPost("clone")]
    [SwaggerOperation(Summary = "Clones a work item.")]
    [ProducesResponseType(typeof(CodeResponse<WorkItemListItemDTO>), 200)]
    [ProducesResponseType(400)]
    [ProducesResponseType(401)]
    [ProducesResponseType(403)]
    [ProducesResponseType(500)]
    public async Task<IActionResult> CloneWorkItem([FromBody] CloneWorkItemRequest cloneWorkItemRequest)
    {
        var userName = JwtTokenHelper.GetUserName(HttpContext, logger, out var actionResult);
        if (string.IsNullOrEmpty(userName))
        {
            return actionResult;
        }

        try
        {
            var result = await workItemService.CloneWorkItemAsync(cloneWorkItemRequest, userName);
            var response = ResponseMapper<WorkItemListItemDTO>.MapServiceResponse(result);
            return response;
        }
        catch (Exception ex)
        {
            logger.LogError(ex, "An error occurred while cloning work item for user {UserName}.", userName);
            return StatusCode(500, "An error occurred while cloning the work item.");
        }
    }

    /// <summary>
    /// Forwards a work-item.
    /// </summary>
    /// <param name="forwardWorkItemRequest">Request model for forward work item.</param>
    /// <returns>String with response.</returns>
    /// <response code="200">Returns String with response.</response>
    /// <response code="400">If the work item reference ID or action is invalid.</response>
    /// <response code="401">If the user is unauthorized.</response>
    /// <response code="500">If an internal server error occurs.</response>
    [HttpPatch("forward")]
    [SwaggerOperation(Summary = "Forwards a work-item.")]
    [ProducesResponseType(typeof(CodeResponse<WorkItemListItemDTO>), 200)]
    [ProducesResponseType(400)]
    [ProducesResponseType(401)]
    [ProducesResponseType(500)]
    public async Task<IActionResult> ForwardWorkItem([FromBody] ForwardWorkItemRequest forwardWorkItemRequest)
    {
        var userName = JwtTokenHelper.GetUserName(HttpContext, logger, out var actionResult);

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

        try
        {
            var result = await workItemService.ForwardWorkItemAsync(forwardWorkItemRequest, userName);
            var response = ResponseMapper<WorkItemListItemDTO>.MapServiceResponse(result);
            return response;
        }
        catch (Exception ex)
        {
            logger.LogError(ex, "An error occurred while forwarding work item for user {UserName}.", userName);
            return StatusCode(500, "An error occurred while forwarding the work item.");
        }
    }

    /// <summary>
    /// Locks a work-item for a specific user.
    /// </summary>
    /// <param name="workItemReferenceId">The ID of the work item reference.</param>
    /// <returns>String with response.</returns>
    /// <response code="200">Returns String with response.</response>
    /// <response code="400">If the work item reference ID is invalid.</response>
    /// <response code="401">If the user is unauthorized.</response>
    /// <response code="500">If an internal server error occurs.</response>
    [HttpPatch("{workItemReferenceId:int}/lock")]
    [SwaggerOperation(Summary = "Locks a work-item for a specific user.")]
    [ProducesResponseType(200)]
    [ProducesResponseType(400)]
    [ProducesResponseType(401)]
    [ProducesResponseType(500)]
    public async Task<IActionResult> LockWorkItem([FromRoute] int workItemReferenceId)
    {
        var userName = JwtTokenHelper.GetUserName(HttpContext, logger, out var actionResult);

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

        try
        {
            var result = await workItemService.LockWorkItemAsync(workItemReferenceId, userName);

            if (string.IsNullOrEmpty(result.ResponseModel))
            {
                logger.LogWarning("Failed to lock work item {WorkItemId} for user {UserName}.", workItemReferenceId, userName);
                return BadRequest("Work item could not be locked.");
            }

            logger.LogInformation("Work item {WorkItemId} successfully locked by user {UserName}.", workItemReferenceId, userName);
            return Ok(result);
        }
        catch (Exception ex)
        {
            logger.LogError(ex, "An error occurred while locking work item {WorkItemId} for user {UserName}.", workItemReferenceId, userName);
            return StatusCode(500, "An error occurred while locking the work item.");
        }
    }

    /// <summary>
    /// Checks if work item is locked for a specific user.
    /// </summary>
    /// <param name="workItemReferenceId">The ID of the work item reference.</param>
    /// <returns>Boolean value that indicates if workitem is locked for current user</returns>
    /// <response code="200">Returns bool with response.</response>
    /// <response code="400">If the work item reference ID is invalid.</response>
    /// <response code="401">If the user is unauthorized.</response>
    /// <response code="500">If an internal server error occurs.</response>
    [HttpGet("{workItemReferenceId:int}/isWorkitemLocked")]
    [SwaggerOperation(Summary = "Checks if work item is locked for a specific user.")]
    [ProducesResponseType(200)]
    [ProducesResponseType(400)]
    [ProducesResponseType(401)]
    [ProducesResponseType(500)]
    public async Task<IActionResult> IsLockedForUserByWorkItemReferenceId([FromRoute] int workItemReferenceId)
    {
        var userName = JwtTokenHelper.GetUserName(HttpContext, logger, out var actionResult);

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

        try
        {
            var result = await workItemService.IsLockedForUserByWorkItemReferenceId(workItemReferenceId, userName);

            logger.LogInformation("Returned bool value that indicates if work item {WorkItemId} successfully locked by user {UserName}.", workItemReferenceId, userName);
            return Ok(result);
        }
        catch (NotFoundException ex)
        {
            return BadRequest(ex.Message);
        }
        catch (Exception ex)
        {
            logger.LogError(ex, "An error occurred while returning bool value that indicates if work item {WorkItemId} successfully locked by user {UserName}.", workItemReferenceId, userName);
            return StatusCode(500, "An error occurred while checking work items lock.");
        }
    }

    /// <summary>
    /// Unlocks a work-item.
    /// </summary>
    /// <param name="workItemReferenceId">The ID of the work item reference.</param>
    /// <returns>String with response.</returns>
    /// <response code="200">Returns String with response.</response>
    /// <response code="400">If the work item reference ID is invalid.</response>
    /// <response code="401">If the user is unauthorized.</response>
    /// <response code="500">If an internal server error occurs.</response>
    [HttpPatch("{workItemReferenceId:int}/unlock")]
    [SwaggerOperation(Summary = "Unlocks a work-item.")]
    [ProducesResponseType(200)]
    [ProducesResponseType(400)]
    [ProducesResponseType(401)]
    [ProducesResponseType(500)]
    public async Task<IActionResult> UnlockWorkItem([FromRoute] int workItemReferenceId)
    {
        var userName = JwtTokenHelper.GetUserName(HttpContext, logger, out var actionResult);

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

        try
        {
            var result = await workItemService.UnlockWorkItemAsync(workItemReferenceId);

            if (string.IsNullOrEmpty(result.ResponseModel))
            {
                logger.LogWarning("Failed to unlock work item {WorkItemId}.", workItemReferenceId);
                return BadRequest("Work item could not be unlocked.");
            }

            logger.LogInformation("Work item {WorkItemId} successfully unlocked by user {UserName}.", workItemReferenceId, userName);
            return Ok(result);
        }
        catch (Exception ex)
        {
            logger.LogError(ex, "An error occurred while unlocking work item {WorkItemId} for user {UserName}.", workItemReferenceId, userName);
            return StatusCode(500, "An error occurred while unlocking the work item.");
        }
    }

    /// <summary>
    /// Renames a work item.
    /// </summary>
    /// <param name="renameWorkItemRequest">Contains new name and work item id</param>
    /// <returns>New name</returns>
    [HttpPatch("rename")]
    public async Task<IActionResult> RenameWorkItem([FromBody] RenameWorkItemRequest renameWorkItemRequest)
    {
        var userName = JwtTokenHelper.GetUserName(HttpContext, logger, out var actionResult);
        if (string.IsNullOrEmpty(userName))
        {
            return actionResult; // Unauthorized user
        }
        try
        {
            var result = await workItemService.RenameWorkItemAsync(renameWorkItemRequest.NewName, renameWorkItemRequest.WorkItemReferenceId);
            return Ok(result);
        }
        catch (Exception ex)
        {
            logger.LogError(ex, "An error occurred while renaming work item for user {UserName}.", userName);
            return StatusCode(500, "An error occurred while renaming the work item.");
        }
    }

    /// <summary>
    /// Deletes work items and all dependent data.
    /// </summary>
    /// <param name="request">List of WorkItemIds to delete.</param>
    /// <returns>Status and summary message.</returns>
    /// <response code="200">Delete completed (idempotent if items already gone).</response>
    /// <response code="400">No ids supplied or invalid payload.</response>
    /// <response code="401">Unauthorized.</response>
    /// <response code="500">Internal error.</response>
    [HttpPost("delete")]
    [Authorize(Roles = nameof(UserGroups.PDAdminGroup))]
    [SwaggerOperation(Summary = "Deletes work items and related data (EF, one transaction).")]
    [ProducesResponseType(typeof(Response<string>), 200)]
    [ProducesResponseType(400)]
    [ProducesResponseType(401)]
    [ProducesResponseType(500)]
    public async Task<IActionResult> DeleteWorkItems([FromBody] List<int> workItemIds, CancellationToken ct)
    {
        var userName = JwtTokenHelper.GetUserName(HttpContext, logger, out var actionResult);
        if (string.IsNullOrEmpty(userName))
            return actionResult;

        if (workItemIds == null || workItemIds.Count == 0)
            return BadRequest("WorkItemIds are required.");

        try
        {
            logger.LogInformation(
                "User {UserName} requested deletion of {Count} work items: {Ids}",
                userName, workItemIds.Count, string.Join(",", workItemIds));
            var result = await workItemService.DeleteWorkItemsAsync(workItemIds, ct);

            if (!result.Success)
                return BadRequest(result.Message);

            logger.LogInformation(
                "User {UserName} deleted {Count} work items: {Ids}",
                userName, workItemIds.Count, string.Join(",", workItemIds));

            return Ok(result);
        }
        catch (Exception ex)
        {
            logger.LogError(ex, "Error deleting work items for user {UserName}.", userName);
            return StatusCode(500, "An error occurred while deleting work items.");
        }
    }
}