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;

[Route("notices")]
public class NoticeController : ControllerBase
{
    private readonly INoticeService _noticeService;


    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>
    [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>
    [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>
    [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>
    [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>
    [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>
    [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();
    }
}