using Microsoft.AspNetCore.Mvc;
using DigitalData.Core.Abstraction.Application;
using DigitalData.Core.Abstraction.Application.DTO;

namespace DigitalData.Core.API
{
    /// <summary>
    /// A base controller class that provides generic CRUD (Create, Read, Update, Delete) operations for a specified entity type,
    /// with enhanced error handling to ensure robust and reliable API endpoints.
    /// </summary>
    /// <typeparam name="TCRUDService">The derived CRUD service type implementing ICRUDService<TCreateDto, TReadDto, TUpdateDto, TEntity, TId>.</typeparam>
    /// <typeparam name="TCreateDto">The Data Transfer Object type for create operations.</typeparam>
    /// <typeparam name="TReadDto">The Data Transfer Object type for read operations.</typeparam>
    /// <typeparam name="TEntity">The entity type CRUD operations will be performed on.</typeparam>
    /// <typeparam name="TId">The type of the entity's identifier.</typeparam>
    [ApiController]
    [Route("api/[controller]")]
    [Obsolete("Use MediatR")]
    public class CRUDControllerBaseWithErrorHandling<TCRUDService, TCreateDto, TReadDto, TUpdateDto, TEntity, TId> : ControllerBase
        where TCRUDService : ICRUDService<TCreateDto, TReadDto, TEntity, TId>
        where TCreateDto : class
        where TReadDto : class
        where TUpdateDto : class
        where TEntity : class
    {
        protected readonly ILogger _logger;
        protected readonly TCRUDService _service;

        /// <summary>
        /// Initializes a new instance of the CRUDControllerBase class with specified logger and CRUD service.
        /// </summary>
        /// <param name="logger">The logger to be used by the controller.</param>
        /// <param name="service">The CRUD service handling business logic for the entity.</param>
        public CRUDControllerBaseWithErrorHandling(
            ILogger logger,
            TCRUDService service)
        {
            _logger = logger ?? throw new ArgumentNullException(nameof(logger));
            _service = service ?? throw new ArgumentNullException(nameof(service));
        }

        /// <summary>
        /// Creates a new entity based on the provided DTO.
        /// </summary>
        /// <param name="createDto">The DTO from which to create the entity.</param>
        /// <returns>A task that represents the asynchronous create operation. The task result contains the action result.</returns>
        [HttpPost]
        public virtual async Task<IActionResult> Create(TCreateDto createDto)
        {
            try
            {
                return await _service.CreateAsync(createDto).ThenAsync<TReadDto, IActionResult>(
                                Success: dto =>
                                {
                                    var createdResource = new { Id = dto.GetId() };
                                    var actionName = nameof(GetById);
                                    var routeValues = new { id = createdResource.Id };
                                    return CreatedAtAction(actionName, routeValues, createdResource);
                                },
                                Fail: (messages, notices) =>
                                {
                                    _logger.LogNotice(notices);
                                    return BadRequest(messages);
                                });
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "{Message}", ex.Message);
                return StatusCode(StatusCodes.Status500InternalServerError);
            }
        }

        /// <summary>
        /// Retrieves an entity by its identifier.
        /// </summary>
        /// <param name="id">The identifier of the entity to retrieve.</param>
        /// <returns>A task that represents the asynchronous read operation. The task result contains the action result.</returns>
        [HttpGet("{id}")]
        public virtual async Task<IActionResult> GetById([FromRoute] TId id)
        {
            try
            {
                return await _service.ReadByIdAsync(id).ThenAsync(
                    Success: Ok,
                    Fail: IActionResult (messages, notices) =>
                    {
                        _logger.LogNotice(notices);
                        return NotFound(messages);
                    });
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "{Message}", ex.Message);
                return StatusCode(StatusCodes.Status500InternalServerError);
            }
        }

        /// <summary>
        /// Retrieves all entities.
        /// </summary>
        /// <returns>A task that represents the asynchronous read-all operation. The task result contains the action result.</returns>
        [HttpGet]
        public virtual async Task<IActionResult> GetAll()
        {
            try
            {
                return await _service.ReadAllAsync().ThenAsync(
                    Success: Ok,
                    Fail: IActionResult (messages, notices) =>
                    {
                        _logger.LogNotice(notices);
                        return NotFound(messages);
                    });
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "{Message}", ex.Message);
                return StatusCode(StatusCodes.Status500InternalServerError);
            }
        }

        /// <summary>
        /// Updates an existing entity based on the provided DTO.
        /// </summary>
        /// <param name="updateDto">The DTO containing the updated data for the entity.</param>
        /// <returns>A task that represents the asynchronous update operation. The task result contains the action result.</returns>
        [HttpPut]
        public virtual async Task<IActionResult> Update(TUpdateDto updateDto)
        {
            try
            {
                return await _service.UpdateAsync(updateDto).ThenAsync(
                                Success: Ok,
                                Fail: IActionResult (messages, notices) =>
                                {
                                    _logger.LogNotice(notices);
                                    return BadRequest(messages);
                                });
            }
            catch(Exception ex)
            {
                _logger.LogError(ex, "{Message}", ex.Message);
                return StatusCode(StatusCodes.Status500InternalServerError);
            }
        }

        /// <summary>
        /// Deletes an entity by its identifier.
        /// </summary>
        /// <param name="id">The identifier of the entity to delete.</param>
        /// <returns>A task that represents the asynchronous delete operation. The task result contains the action result.</returns>
        [HttpDelete("{id}")]
        public virtual async Task<IActionResult> Delete([FromRoute] TId id)
        {
            try
            {
                return await _service.DeleteAsyncById(id).ThenAsync(
                                Success: Ok,
                                Fail: IActionResult (messages, notices) =>
                                {
                                    _logger.LogNotice(notices);
                                    return BadRequest(messages);
                                });
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "{Message}", ex.Message);
                return StatusCode(StatusCodes.Status500InternalServerError);
            }
        }
    }
}