using adas_core.Application.Exceptions;
using adas_core.Application.Services.Interfaces;
using adas_core.Authentication.Attributes;
using adas_core.Domain.Enums;
using adas_core.Domain.Models.MongoModels;
using adas_core.Logging;
using Microsoft.AspNetCore.Mvc;
using MongoDB.Bson;

namespace adas_core.Controllers;

/// <summary>
/// Serves as the API controller responsible for handling HTTP requests routed to the <c>notices</c> endpoint, deriving from <see cref="ControllerBase"/>.
/// </summary>
/// <remarks>
/// The controller is mapped to the route template <c>notices</c> via the <see cref="RouteAttribute"/> applied at the class level.</remarks>
/// <!-- aidoc:v1 sig=e514819 -->
[Route("notices")]
public class NoticeController : ControllerBase
{
    private readonly INoticeService _noticeService;


    /// <summary>
    /// Initializes a new instance of the <see cref="NoticeController"/> class, a controller that handles notice operations via the injected <paramref name="noticeService"/>, and sets a trace identifier on <see cref="LogExecutionContext"/> for the current execution context.
    /// </summary>
    /// <param name="noticeService">The <see cref="INoticeService"/> dependency used by the controller to perform notice-related operations.</param>
    /// <!-- aidoc:v1 sig=74adc42 body=87a81c4 -->
    public NoticeController(INoticeService noticeService)
    {
        LogExecutionContext.TrySetTraceIdentifier(Guid.NewGuid().ToString(), true);

        _noticeService = noticeService;
    }


    /// <summary>
    /// Retrieves all notices for the authorized source, requiring the "DisplayId" permission under the "Source" resource and the AuthSome role.
    /// </summary>
    /// <returns>An <see cref="IActionResult"/> containing the list of notices returned by the notice service, wrapped in an HTTP 200 OK response.</returns>
    /// <!-- aidoc:v1 sig=d479c92 body=309ff47 -->
    [HttpGet]
    [AuthorizeRoles(PermissionEnum.RolesType.AuthSome)]
    [PermissionAuthorize("Source", "DisplayId")]
    public async Task<IActionResult> GetAllNotices()
    {
        var result = await _noticeService.GetNoticesAsync();
        return Ok(result);
    }

    /// <summary>
    /// Retrieves notices filtered by the specified type, returning the matching results as an HTTP 200 response.
    /// </summary>
    /// <param name="type">The notice type used to filter and retrieve the relevant notices.</param>
    /// <returns>An <see cref="IActionResult"/> containing the notices that match the specified type.</returns>
    /// <!-- aidoc:v1 sig=bafe528 body=d087c2a -->
    [HttpGet("{type}/type")]
    [AuthorizeRoles(PermissionEnum.RolesType.AuthSome)]
    [PermissionAuthorize("Source", "DisplayId")]
    public async Task<IActionResult> GetNoticesByType(string type)
    {
        var result = await _noticeService.GetNoticeByTypeAsync(type);
        return Ok(result);
    }

    /// <summary>
    /// Retrieves a notice identified by the given id and returns it in the response body.
    /// </summary>
    /// <param name="id">The identifier of the notice to retrieve.</param>
    /// <returns>An <see cref="IActionResult"/> containing the notice data returned by the notice service.</returns>
    /// <!-- aidoc:v1 sig=c30fcd8 body=c0e24e0 -->
    [HttpGet("{id}")]
    [AuthorizeRoles(PermissionEnum.RolesType.AuthSome)]
    [PermissionAuthorize("Source", "DisplayId")]
    public async Task<IActionResult> GetNoticesById(string id)
    {
        var result = await _noticeService.GetNoticeByTypeAsync(id);

        return Ok(result);
    }

    /// <summary>
    /// Creates a new notice associated with the specified display identifier.
    /// Validates the header-provided display identifier format before delegating the insert operation to the notice service.
    /// </summary>
    /// <param name="notice">The notice payload to be created, provided in the request body.</param>
    /// <param name="displayId">The display identifier supplied via the request header, which must be a valid ObjectId.</param>
    /// <returns>An <see cref="IActionResult"/> containing the result produced by the notice service insert operation.</returns>
    /// <exception cref="BadRequestException">Thrown when the <paramref name="displayId"/> header value is not a valid ObjectId format.</exception>
    /// <!-- aidoc:v1 sig=7416ebc body=1b58ae3 -->
    [HttpPost]
    [AuthorizeRoles(PermissionEnum.RolesType.AuthSome)]
    [PermissionAuthorize("Source", "DisplayId")]
    public async Task<IActionResult> InsertNotice([FromBody] Notice notice, [FromHeader] string displayId)
    {
        if (!ObjectId.TryParse(displayId, out var displayIdParsed))
            throw new BadRequestException(HttpEnum.ErrorMessage.BadRequestInvalidFormat);
        notice.DisplayId = displayIdParsed;
        var result = await _noticeService.InsertNotice(notice);
        return Ok(result);
    }

    /// <summary>
    /// Updates an existing notice identified by the route <paramref name="id"/>, after validating that the id is a valid object identifier format.
    /// </summary>
    /// <param name="id">The string identifier of the notice to update, provided in the route.</param>
    /// <param name="notice">The notice payload containing the updated information.</param>
    /// <returns>An <see cref="IActionResult"/> that produces a 200 OK response when the update succeeds.</returns>
    /// <exception cref="BadRequestException">Thrown when <paramref name="id"/> is not in a valid object identifier format.</exception>
    /// <!-- aidoc:v1 sig=d17dfb8 body=24d1bad -->
    [HttpPut("{id}")]
    [AuthorizeRoles(PermissionEnum.RolesType.AuthSome)]
    [PermissionAuthorize("Source", "DisplayId")]
    public async Task<IActionResult> UpdateNotice(string id, [FromBody] Notice notice)
    {
        if (!ObjectId.TryParse(id, out var noticeId))
            throw new BadRequestException(HttpEnum.ErrorMessage.BadRequestInvalidFormat);
        notice.Id = noticeId;
        await _noticeService.UpdateNoticeAsync(notice);
        return Ok();
    }

    /// <summary>
    /// Deletes a notice by its identifier after validating the ObjectId format.
    /// </summary>
    /// <param name="id">The string representation of the notice's ObjectId to be deleted.</param>
    /// <returns>An <see cref="IActionResult"/> indicating the result of the delete operation.</returns>
    /// <exception cref="BadRequestException">Thrown when the provided <paramref name="id"/> is not a valid ObjectId format.</exception>
    /// <!-- aidoc:v1 sig=32e01f3 body=656f369 -->
    [HttpDelete("{id}")]
    [AuthorizeRoles(PermissionEnum.RolesType.AuthSome)]
    [PermissionAuthorize("Source", "DisplayId")]
    public async Task<IActionResult> DeleteNotice(string id)
    {
        if (!ObjectId.TryParse(id, out var noticeId))
            throw new BadRequestException(HttpEnum.ErrorMessage.BadRequestInvalidFormat);
        await _noticeService.DeleteNoticeByIdAsync(noticeId);
        return Ok();
    }
}