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("discharges")]
[ApiController]
public class DischargeController : ControllerBase
{
    private readonly IDischargeService _dischargeService;
    private readonly IPatientService _patientService;

    public DischargeController(IDischargeService dischargeService, IPatientService patientService)
    {
        LogExecutionContext.TrySetTraceIdentifier(Guid.NewGuid().ToString(), true);

        _dischargeService = dischargeService;
        _patientService = patientService;
    }

    /// <summary>
    /// Retrieves all discharge records available in the system.
    /// </summary>
    /// <returns>An <see cref="IActionResult"/> containing the collection of discharges with an HTTP 200 OK response.</returns>
    [HttpGet]
    [AuthorizeRoles(PermissionEnum.RolesType.AuthSome)]
    [PermissionAuthorize("Source", "DisplayId")]
    public async Task<IActionResult> GetAllDischarges()
    {
        var discharges = await _dischargeService.GetDischargesAsync();
        return Ok(discharges);
    }

    /// <summary>
    /// Retrieves a discharge record by its identifier, applying role-based and source permission authorization.
    /// Validates the format of the provided id before delegating the lookup to the discharge service.
    /// </summary>
    /// <param name="id">The string identifier of the discharge to retrieve, expected to be a valid ObjectId.</param>
    /// <returns>An <see cref="IActionResult"/> containing the discharge data when found.</returns>
    /// <exception cref="BadRequestException">Thrown when the provided <paramref name="id"/> cannot be parsed as a valid ObjectId.</exception>
    [HttpGet("{id}")]
    [AuthorizeRoles(PermissionEnum.RolesType.AuthSome)]
    [PermissionAuthorize("Source", "DisplayId")]
    public async Task<IActionResult> GetDischargebyId(string id)
    {
        if (!ObjectId.TryParse(id, out var dischargeId))
            throw new BadRequestException(HttpEnum.ErrorMessage.BadRequestInvalidFormat);
        var discharge = await _dischargeService.GetDischargeByIdAsync(dischargeId);
        return Ok(discharge);
    }

    /// <summary>
    /// Records the discharge of a patient by associating the provided discharge data with the corresponding patient record.
    /// Requires the caller to hold the authorized role and the permission for the specified source and display ID.
    /// </summary>
    /// <param name="discharge">The discharge information, including the patient identifier, to be persisted.</param>
    /// <returns>An <see cref="IActionResult"/> that returns an OK response when the discharge is successfully recorded.</returns>
    /// <exception cref="BadRequestException">Thrown when the <paramref name="discharge"/> parameter is null, indicating missing required parameters.</exception>
    [HttpPost("patient")]
    [AuthorizeRoles(PermissionEnum.RolesType.AuthSome)]
    [PermissionAuthorize("Source", "DisplayId")]
    public async Task<IActionResult> DischargePatient([FromBody] Discharge discharge)
    {
        if (discharge == null) throw new BadRequestException(HttpEnum.ErrorMessage.BadRequestMissingParameters);
        var patient = await _patientService.FindById(discharge.PatientId);
        discharge.Patient = patient;

        await _dischargeService.InsertDischarge(discharge);
        return Ok();
    }

    /// <summary>
    /// Updates an existing discharge record, preserving the existing <c>UnitId</c> when the incoming payload omits it.
    /// </summary>
    /// <param name="discharge">The discharge entity supplied in the request body, identified by its <c>Id</c>.</param>
    /// <returns>An <see cref="IActionResult"/> that yields an <c>Ok</c> response on successful update.</returns>
    /// <exception cref="BadRequestException">Thrown when the supplied <paramref name="discharge"/> payload is <c>null</c>.</exception>
    [HttpPut]
    [AuthorizeRoles(PermissionEnum.RolesType.AuthSome)]
    [PermissionAuthorize("Source", "DisplayId")]
    public async Task<IActionResult> UpdateDischarge([FromBody] Discharge discharge)
    {
        if (discharge == null) throw new BadRequestException(HttpEnum.ErrorMessage.BadRequestMissingParameters);
        var c = await _dischargeService.GetDischargeByIdAsync(discharge.Id);
        if (discharge.UnitId == null && c?.UnitId != null) discharge.UnitId = c.UnitId;
        await _dischargeService.UpdateDischargeAsync(discharge);

        return Ok();
    }

    /// <summary>
    /// Inserts a new discharge record by delegating to the discharge service.
    /// Validates that the supplied discharge payload is not null before processing and returns the created discharge on success.
    /// </summary>
    /// <param name="discharge">The discharge entity to insert, provided in the request body.</param>
    /// <returns>An <see cref="IActionResult"/> containing the inserted discharge wrapped in an HTTP 200 OK response.</returns>
    /// <exception cref="BadRequestException">Thrown when the <paramref name="discharge"/> parameter is null, indicating missing required parameters.</exception>
    [HttpPost]
    [AuthorizeRoles(PermissionEnum.RolesType.AuthSome)]
    [PermissionAuthorize("Source", "DisplayId")]
    public async Task<IActionResult> InsertDischarge([FromBody] Discharge discharge)
    {
        if (discharge == null) throw new BadRequestException(HttpEnum.ErrorMessage.BadRequestMissingParameters);
        var dis = await _dischargeService.InsertDischarge(discharge);

        return Ok(dis);
    }

    /// <summary>
    /// Deletes a discharge record by its identifier. Validates the id format, retrieves the discharge and its associated patient, and only removes the discharge when the patient's <c>Altable</c> option type is <c>Altable</c>; otherwise the operation is rejected as a conflict.
    /// </summary>
    /// <param name="id">The string representation of the discharge's identifier to be parsed and deleted.</param>
    /// <returns>An <see cref="IActionResult"/> representing the result of the delete operation.</returns>
    /// <exception cref="BadRequestException">Thrown when the provided <paramref name="id"/> is not a valid ObjectId format.</exception>
    /// <exception cref="ConflictException">Thrown when the patient's <c>Altable</c> option type is not <c>Altable</c>, preventing the deletion.</exception>
    [HttpDelete("{id}")]
    [AuthorizeRoles(PermissionEnum.RolesType.AuthSome)]
    [PermissionAuthorize("Source", "DisplayId")]
    public async Task<IActionResult> DeleteDischargeById(string id)
    {
        if (!ObjectId.TryParse(id, out var dischargeId))
            throw new BadRequestException(HttpEnum.ErrorMessage.BadRequestInvalidFormat);
        var discharge = await _dischargeService.GetDischargeByIdAsync(dischargeId);
        var patient = await _patientService.FindById(discharge!.PatientId);
        discharge.Patient = patient;
        if ("Altable".Equals(patient!.Altable?.OptionType))
        {
            await _dischargeService.DeleteDischargeAsync(discharge);
            return Ok();
        }

        throw new ConflictException(HttpEnum.ErrorMessage.ConflictOperationIncompleted);
    }
}