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.Filter;
using adas_core.Domain.Models.MongoModels;
using Microsoft.AspNetCore.Mvc;
using MongoDB.Bson;

namespace adas_core.Controllers;

[Route("point-of-cares")]
[ApiController]
public class PointOfCareController(IPointOfCareService pointOfCareService)
    : ControllerBase
{
    /// <summary>
    ///     GetByCodeSysAndCode all PoC list
    /// </summary>
    /// <returns>
    ///     List<Unit />
    /// </returns>
    [HttpGet]
    [AuthorizeRoles(PermissionEnum.RolesType.AuthSome)]
    [PermissionAuthorize("Source", "DisplayId")]
    public async Task<IActionResult> List()
    {
        return Ok(await pointOfCareService.GetAll());
    }

    /// <summary>
    ///     GetByCodeSysAndCode all Viirtual PoC list
    /// </summary>
    /// <returns>
    ///     List<Unit />
    /// </returns>
    [HttpGet("get-all-virtual")]
    [AuthorizeRoles(PermissionEnum.RolesType.AuthSome)]
    public IActionResult VirtualPocList()
    {
        var virtualPocNames = Enum.GetNames(typeof(VirtualPointOfCare)).ToList();
        return Ok(virtualPocNames);
    }

    /// <summary>
    /// Retrieves a paginated list of Points of Care (PoCs) based on the provided pagination filter.
    /// Requires authorization with the appropriate role and permission scope.
    /// </summary>
    /// <param name="request">The pagination filter containing the paging criteria to apply to the PoCs query.</param>
    /// <returns>An <see cref="IActionResult"/> containing the paginated PoCs returned by the service.</returns>
    /// <exception cref="BadRequestException">Thrown when the <paramref name="request"/> is null.</exception>
    [HttpPost("paginated")]
    [AuthorizeRoles(PermissionEnum.RolesType.AuthSome)]
    [PermissionAuthorize("Source", "DisplayId")]
    public async Task<IActionResult> GetPaginatedPoCs([FromBody] PaginationFilter request)
    {
        if (request == null) throw new BadRequestException(HttpEnum.ErrorMessage.BadRequestMissingParameters);

        var pocs = await pointOfCareService.GetPaginatedPoCs(request);
        return Ok(pocs);
    }

    /// <summary>
    /// Creates a new point of care record by delegating to the point of care service for persistence and returns the result of the insertion operation.
    /// </summary>
    /// <param name="pointOfCare">The point of care entity supplied in the request body to be created.</param>
    /// <returns>An <see cref="IActionResult"/> containing the result of the insertion wrapped in an HTTP 200 OK response.</returns>
    [HttpPost]
    [AuthorizeRoles(PermissionEnum.RolesType.AuthSome)]
    [PermissionAuthorize("Source", "DisplayId")]
    public async Task<IActionResult> CreateNewPointOfCare([FromBody] PointOfCare pointOfCare)
    {
        var result = await pointOfCareService.InsertPointOfCare(pointOfCare);
        return Ok(result);
    }


    /// <summary>
    /// Updates the status of a point of care, validating that the id is a valid ObjectId and the status maps to a valid <see cref="StatusEnum.PointOfCare"/> value before delegating to the point of care service.
    /// </summary>
    /// <param name="id">The string representation of the point of care identifier to update.</param>
    /// <param name="status">The string representation of the new status to apply.</param>
    /// <returns>An <see cref="IActionResult"/> indicating the result of the status update operation.</returns>
    /// <exception cref="BadRequestException">Thrown when <paramref name="id"/> is not a valid ObjectId or <paramref name="status"/> cannot be parsed as a <see cref="StatusEnum.PointOfCare"/> value.</exception>
    [HttpPut("{id}/{status}/status")]
    [AuthorizeRoles(PermissionEnum.RolesType.AuthSome)]
    [PermissionAuthorize("Source", "DisplayId")]
    public async Task<IActionResult> SetPointOfCareStatus(string id, string status)
    {
        if (!Enum.TryParse(status, out StatusEnum.PointOfCare pocStatus) || !ObjectId.TryParse(id, out var pocId))
            throw new BadRequestException(HttpEnum.ErrorMessage.BadRequestInvalidFormat);
        await pointOfCareService.SetPointOfCareStatus(pocId, pocStatus);

        return Ok();
    }

    /// <summary>
    /// Retrieves all Points of Care (PoC) associated with the specified unit identifier.
    /// </summary>
    /// <param name="id">The unit identifier expected to be a valid ObjectId.</param>
    /// <returns>An HTTP 200 response containing the list of Points of Care for the unit, or null if no results are found.</returns>
    /// <exception cref="BadRequestException">Thrown when the provided <paramref name="id"/> is not a valid ObjectId format.</exception>
    [HttpGet("{id}/unit-id")]
    [AuthorizeRoles(PermissionEnum.RolesType.AuthSome)]
    [PermissionAuthorize("Source", "DisplayId")]
    public async Task<IActionResult> GetAllPoCByUnitId(string id)
    {
        if (!ObjectId.TryParse(id, out var unitId))
            throw new BadRequestException(HttpEnum.ErrorMessage.BadRequestInvalidFormat);

        var result = await pointOfCareService.FindAllByUnitId(unitId);
        return Ok(result?.ToList());
    }

    /// <summary>
    /// Retrieves Point of Care records filtered by the specified unit identifier and status.
    /// Throws a bad request when the unit identifier is not a valid ObjectId or the status cannot be parsed into a known <see cref="StatusEnum.PointOfCare"/> value.
    /// </summary>
    /// <param name="unitId">The string representation of the unit's ObjectId used to locate the associated Point of Care records.</param>
    /// <param name="status">The string representation of the status filter, parsed into the <see cref="StatusEnum.PointOfCare"/> enumeration.</param>
    /// <returns>An <see cref="IActionResult"/> containing the Point of Care records that match the provided unit and status.</returns>
    /// <exception cref="BadRequestException">Thrown when <paramref name="unitId"/> is not a valid ObjectId or <paramref name="status"/> cannot be parsed into a <see cref="StatusEnum.PointOfCare"/> value.</exception>
    [HttpGet("{unitId}/unit-id/{status}/status")]
    [AuthorizeRoles(PermissionEnum.RolesType.AuthSome)]
    [PermissionAuthorize("Source", "DisplayId")]
    public async Task<IActionResult> GetPoCByUnitIdAndStatus(string unitId, string status)
    {
        if (!ObjectId.TryParse(unitId, out var unitIdParsed) ||
            !Enum.TryParse(status, out StatusEnum.PointOfCare pocStatus))
            throw new BadRequestException(HttpEnum.ErrorMessage.BadRequestInvalidFormat);

        var result = await pointOfCareService.FindByUnitAndStatus(unitIdParsed, pocStatus);
        return Ok(result);
    }

    /// <summary>
    /// Updates an existing Point of Care record.
    /// </summary>
    /// <param name="poc">The Point of Care object containing the updated information, provided in the request body.</param>
    /// <returns>An <see cref="IActionResult"/> containing the result of the update operation.</returns>
    [HttpPut]
    [AuthorizeRoles(PermissionEnum.RolesType.AuthSome)]
    [PermissionAuthorize("Source", "DisplayId")]
    public async Task<IActionResult> UpdatePoC([FromBody] PointOfCare poc)
    {
        var result = await pointOfCareService.Update(poc);
        return Ok(result);
    }

    /// <summary>
    /// Updates the configuration of an existing Point of Care device identified by <paramref name="id"/>.
    /// Validates that the supplied identifier is a valid ObjectId format before delegating the update to the Point of Care service.
    /// </summary>
    /// <param name="id">The identifier of the Point of Care whose configuration will be updated.</param>
    /// <param name="pocConfig">The new configuration payload to apply to the Point of Care.</param>
    /// <returns>An <see cref="IActionResult"/> containing an HTTP 200 OK response when the configuration is successfully updated.</returns>
    /// <exception cref="BadRequestException">Thrown when <paramref name="id"/> cannot be parsed as a valid ObjectId.</exception>
    [HttpPut("{id}/config")]
    [AuthorizeRoles(PermissionEnum.RolesType.AuthSome)]
    [PermissionAuthorize("Source", "DisplayId")]
    public async Task<IActionResult> UpdatePoCConfiguration(string id, [FromBody] PointOfCareConfiguration pocConfig)
    {
        if (!ObjectId.TryParse(id, out var pocId))
            throw new BadRequestException(HttpEnum.ErrorMessage.BadRequestInvalidFormat);
        await pointOfCareService.UpdateConfiguration(pocId, pocConfig);
        return Ok();
    }

    /// <summary>
    /// Updates the relay configuration of a Point of Care (PoC) device based on the provided payload.
    /// </summary>
    /// <param name="poc">The Point of Care object containing the updated relay configuration details.</param>
    /// <returns>An <see cref="IActionResult"/> indicating the result of the operation; returns <see cref="OkResult"/> on successful update.</returns>
    [HttpPut("relay")]
    [AuthorizeRoles(PermissionEnum.RolesType.AuthSome)]
    [PermissionAuthorize("Source", "DisplayId")]
    public async Task<IActionResult> UpdatePoCRelay([FromBody] PointOfCare poc)
    {
        await pointOfCareService.UpdateRelayConfig(poc);
        return Ok();
    }

    /// <summary>
    /// Updates the Point of Care (PoC) unit configuration identified by the supplied identifier.
    /// Validates that the provided identifier is in a valid ObjectId format before delegating the update to the point of care service.
    /// </summary>
    /// <param name="unit">The unit payload containing the configuration changes to apply, supplied in the request body.</param>
    /// <param name="id">The identifier of the PoC whose unit configuration should be updated, expected to be a valid ObjectId.</param>
    /// <returns>An <see cref="IActionResult"/> that produces an HTTP 200 (OK) response when the update succeeds.</returns>
    /// <exception cref="BadRequestException">Thrown when the supplied <paramref name="id"/> cannot be parsed as a valid ObjectId.</exception>
    [HttpPut("{id}/unit")]
    [AuthorizeRoles(PermissionEnum.RolesType.AuthSome)]
    [PermissionAuthorize("Source", "DisplayId")]
    public async Task<IActionResult> UpdatePoCConfiguration([FromBody] Unit unit, string id)
    {
        if (!ObjectId.TryParse(id, out var pocId))
            throw new BadRequestException(HttpEnum.ErrorMessage.BadRequestInvalidFormat);
        await pointOfCareService.UpdateUnit(pocId, unit);
        return Ok();
    }

    /// <summary>
    /// Deletes a Point of Care (PoC) entity identified by the supplied identifier.
    /// </summary>
    /// <param name="id">The string representation of the PoC's ObjectId to be deleted.</param>
    /// <returns>An <see cref="IActionResult"/> representing an HTTP 200 OK response when the deletion succeeds.</returns>
    /// <exception cref="BadRequestException">Thrown when <paramref name="id"/> is not a valid ObjectId format.</exception>
    [HttpDelete("{id}")]
    [AuthorizeRoles(PermissionEnum.RolesType.AuthSome)]
    [PermissionAuthorize("Source", "DisplayId")]
    public async Task<IActionResult> DeletePoC(string id)
    {
        if (!ObjectId.TryParse(id, out var pocId))
            throw new BadRequestException(HttpEnum.ErrorMessage.BadRequestInvalidFormat);
        await pointOfCareService.Delete(pocId);
        return Ok();
    }
}