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

namespace adas_core.Controllers;

[Route("units")]
[ApiController]
public class UnitController(
    ILogger<UnitController> logger,
    IUnitService unitService,
    IAdminPanelService adminPanelService)
    : ControllerBase
{
    /// <summary>
    ///     GetByCodeSysAndCode all unit list
    /// </summary>
    /// <returns>
    ///     List<Unit />
    /// </returns>
    [HttpGet("{withPoCs}")]
    [AuthorizeRoles(PermissionEnum.RolesType.AuthSome)]
    [PermissionAuthorize("Source", "DisplayId")]
    public async Task<IActionResult> List(bool withPoCs)
    {
        var units = await unitService.GetAll(withPoCs);
        return Ok(units);
    }

    /// <summary>
    /// Retrieves a compact list of units, typically used for lightweight UI scenarios such as dropdowns or lookups.
    /// </summary>
    /// <returns>An <see cref="IActionResult"/> containing the compact collection of units on success.</returns>
    [HttpGet("compact")]
    [AuthorizeRoles(PermissionEnum.RolesType.AuthSome)]
    [PermissionAuthorize("Source", "DisplayId")]
    public async Task<IActionResult> ListCompact()
    {
        var units = await unitService.GetAllCompact();
        return Ok(units);
    }

    /// <summary>
    /// Retrieves a compact representation of a unit identified by the given id, requiring the caller to have display permissions on the "Source" entity.
    /// Validates that the supplied id is a well-formed identifier before delegating the lookup to the unit service.
    /// </summary>
    /// <param name="id">The string identifier of the unit to retrieve; must be a parseable identifier or a bad request is raised.</param>
    /// <returns>An <see cref="IActionResult"/> containing the compact unit payload when the id is valid.</returns>
    /// <exception cref="BadRequestException">Thrown when <paramref name="id"/> cannot be parsed as a valid identifier.</exception>
    [HttpGet("compact/{id}")]
    [AuthorizeRoles(PermissionEnum.RolesType.AuthSome)]
    [PermissionAuthorize("Source", "DisplayId")]
    public async Task<IActionResult> Compact(string id)
    {
        if (!ObjectId.TryParse(id, out var idConvertd))
            throw new BadRequestException(HttpEnum.ErrorMessage.BadRequestMissingParameters);
        var unit = await unitService.GetOneCompact(idConvertd);
        return Ok(unit);
    }

    /// <summary>
    /// Retrieves a paginated list of units, optionally including their associated Points of Contact (PoCs), based on the supplied pagination filter.
    /// </summary>
    /// <param name="request">The pagination filter specifying the page size, page number, and any additional filtering criteria.</param>
    /// <param name="withPoCs">A flag indicating whether the response should include the related PoCs for each unit.</param>
    /// <returns>An <see cref="IActionResult"/> containing the paginated units wrapped in an HTTP 200 OK response.</returns>
    /// <exception cref="BadRequestException">Thrown when the <paramref name="request"/> parameter is null, indicating missing required parameters.</exception>
    [HttpPost("paginated/{withPoCs}")]
    [AuthorizeRoles(PermissionEnum.RolesType.AuthSome)]
    [PermissionAuthorize("Source", "DisplayId")]
    public async Task<IActionResult> GetPaginatedUnits([FromBody] PaginationFilter request, bool withPoCs)
    {
        if (request == null) throw new BadRequestException(HttpEnum.ErrorMessage.BadRequestMissingParameters);
        var units = await unitService.GetPaginatedUnits(request, withPoCs);
        return Ok(units);
    }


    /// <summary>
    ///     GetByCodeSysAndCode unit by id
    /// </summary>
    /// <param name="id"></param>
    /// <param name="listFilled"></param>
    /// <returns></returns>
    [HttpGet("{id}/{listFilled}")]
    [AuthorizeRoles(PermissionEnum.RolesType.AuthSome)]
    [PermissionAuthorize("Source", "DisplayId")]
    public async Task<IActionResult> Find(string id, bool listFilled = true)
    {
        if (!ObjectId.TryParse(id, out var unitId))
            throw new BadRequestException(HttpEnum.ErrorMessage.BadRequestInvalidFormat);
        //TODO: ADAPTAR A LOCALE
        var unit = await unitService.GetInfo(unitId, fillLists: listFilled, withPoCs: true,
            dataLocale: LocaleEnum.Default);
        return Ok(unit);
    }

    /// <summary>
    ///     GetByCodeSysAndCode unit status
    /// </summary>
    /// <param name="id"></param>
    /// <returns></returns>
    [HttpGet("status/{id}")]
    [AuthorizeRoles(PermissionEnum.RolesType.AuthSome)]
    [PermissionAuthorize("Source", "DisplayId")]
    public async Task<IActionResult> Status(string id)
    {
        if (!ObjectId.TryParse(id, out var unitId))
            throw new BadRequestException(HttpEnum.ErrorMessage.BadRequestInvalidFormat);
        //TODO: ADAPTAR A LOCALE
        var section = await unitService.GetInfo(unitId, LocaleEnum.Default);

        return Ok(section!.Status);
    }

    /// <summary>
    ///     GetByCodeSysAndCode unit config by unit name
    /// </summary>
    /// <param name="unitName"></param>
    /// <returns></returns>
    [HttpGet("{unitName}/config")]
    [AuthorizeRoles(PermissionEnum.RolesType.AuthSome)]
    [PermissionAuthorize("Source", "DisplayId")]
    public async Task<IActionResult> Config(string unitName)
    {
        //SEND SECTION INFO
        var section = await unitService.GetByName(unitName);
        return Ok(section!.Configuration);
    }

    /// <summary>
    /// </summary>
    /// <param name="patientId"></param>
    /// <returns></returns>
    [HttpGet("{patientId}/patient-id")]
    [AuthorizeRoles(PermissionEnum.RolesType.AuthSome)]
    [PermissionAuthorize("Source", "DisplayId")]
    public async Task<IActionResult> FindByPatientId(string patientId)
    {
        if (!ObjectId.TryParse(patientId, out var objPatientId))
            throw new BadRequestException(HttpEnum.ErrorMessage.BadRequestInvalidFormat);

        var sections = await unitService.FindByPatientId(objPatientId);

        return Ok(sections);
    }

    /// <summary>
    /// Unit PointOfCares
    /// </summary>
    /// <param name="id"></param>
    /// <returns></returns>
    [HttpGet("{id}/point-of-cares")]
    [AuthorizeRoles(PermissionEnum.RolesType.AuthSome)]
    [PermissionAuthorize("Source", "DisplayId")]
    public async Task<IActionResult> GetUnitPointOfCares(string id)
    {
        if (!ObjectId.TryParse(id, out var unitId))
            throw new BadRequestException(HttpEnum.ErrorMessage.BadRequestInvalidFormat);
        //TODO: ADAPTAR A LOCALE
        var sec = await unitService.GetInfo(unitId);
        return Ok(sec?.PointOfCares);
    }

    #region UciPanel

    /// <summary>
    ///     Post unit
    /// </summary>
    /// <param name="unit"></param>
    /// <returns></returns>
    [HttpPost]
    [AuthorizeRoles(PermissionEnum.RolesType.AuthSome)]
    [PermissionAuthorize("Source", "DisplayId")]
    public async Task<IActionResult> PostUnit(Unit unit)
    {
        var sec = await unitService.FindById(unit.Id);
        if (sec != null) throw new ConflictException(HttpEnum.ErrorMessage.ConflictResourceAlreadyExists);
        sec = await adminPanelService.InsertUnit(unit);
        return Ok(sec);
    }

    /// <summary>
    ///     Post unit
    /// </summary>
    /// <param name="id"></param>
    /// <returns></returns>
    [HttpDelete("{id}")]
    [AuthorizeRoles(PermissionEnum.RolesType.AuthSome)]
    [PermissionAuthorize("Source", "DisplayId")]
    public async Task<IActionResult> DeleteUnit(string id)
    {
        if (!ObjectId.TryParse(id, out var unitId))
            throw new BadRequestException(HttpEnum.ErrorMessage.BadRequestInvalidFormat);

        var unit = await unitService.FindById(unitId) ??
                   throw new NotFoundException(HttpEnum.ErrorMessage.NotFoundResourceMissing);
        var deleted = await adminPanelService.DeleteUnitById(unit.Id);
        if (deleted)
            return Ok();
        throw new BadRequestException(HttpEnum.ErrorMessage.ConflictDeleteFailed);
    }

    /// <summary>
    ///     Update Unit
    /// </summary>
    /// <param name="id"></param>
    /// <param name="unit"></param>
    /// <returns></returns>
    [HttpPut("{id}")]
    [AuthorizeRoles(PermissionEnum.RolesType.AuthSome)]
    [PermissionAuthorize("Source", "DisplayId")]
    public async Task<IActionResult> UpdateSection(string id, [FromBody] Unit unit)
    {
        if (!ObjectId.TryParse(id, out var unitId))
            throw new BadRequestException(HttpEnum.ErrorMessage.BadRequestInvalidFormat);
        //TODO: ADAPTAR A LOCALE
        await unitService.GetInfo(unitId, LocaleEnum.Default, false);
        var secUpdated = await unitService.UpdateUnit(unit);
        logger.LogInformation("Section {sectionid} updated at UTC {DateTimeUtcNow}", unit.Id, DateTime.UtcNow);
        return Ok(secUpdated);
    }

    /// <summary>
    ///     Update Unit master list Id
    /// </summary>
    /// <param name="updateUnitListDto"></param>
    /// <returns></returns>
    [HttpPut("master-list-id")]
    [AuthorizeRoles(PermissionEnum.RolesType.AuthSome)]
    [PermissionAuthorize("Source", "DisplayId")]
    public async Task<IActionResult> UpdateMasterListUnit([FromBody] UpdateUnitIdListDto updateUnitListDto)
    {
        //TODO: ADAPTAR A LOCALE
        await unitService.GetInfo(updateUnitListDto.UnitId, LocaleEnum.Default, false);

        var secUpdated = await unitService.UpdateUnitMasterList(updateUnitListDto);
        logger.LogInformation("Unit {sectionid} updated at UTC {DateTimeUtcNow} for master list {master}",
            updateUnitListDto.UnitId, DateTime.UtcNow, updateUnitListDto.MasterListData.ToString());
        return Ok(secUpdated);
    }

    /// <summary>
    /// Updates the configuration of a unit identified by its id, after validating the id format.
    /// Requires authentication with the <see cref="PermissionEnum.RolesType.AuthSome"/> role and permission to display the "Source" resource.
    /// </summary>
    /// <param name="unitId">The id of the unit whose configuration will be updated, provided in the route.</param>
    /// <param name="unitConfiguration">The new configuration values supplied in the request body.</param>
    /// <returns>An <see cref="IActionResult"/> containing the updated unit configuration when the operation succeeds.</returns>
    /// <exception cref="BadRequestException">Thrown when <paramref name="unitId"/> cannot be parsed as a valid id format.</exception>
    [HttpPut("{unitId}/config")]
    [AuthorizeRoles(PermissionEnum.RolesType.AuthSome)]
    [PermissionAuthorize("Source", "DisplayId")]
    public async Task<IActionResult> UpdateConfiguration(string unitId, [FromBody] UnitConfiguration unitConfiguration)
    {
        var isParsed = ObjectId.TryParse(unitId, out var unitIdParsed);
        if (!isParsed) throw new BadRequestException(HttpEnum.ErrorMessage.BadRequestInvalidFormat);
        var secUpdated = await unitService.UpdateConfiguration(unitIdParsed, unitConfiguration);
        return Ok(secUpdated);
    }

    /// <summary>
    ///     Update Unit
    /// </summary>
    /// <param name="request"></param>
    /// <param name="id"></param>
    /// <returns></returns>
    [HttpPut("{id}/info")]
    [AuthorizeRoles(PermissionEnum.RolesType.AuthSome)]
    [PermissionAuthorize("Source", "DisplayId")]
    public async Task<IActionResult> UpdateUnitInfo([FromBody] UnitInfoDto request, string id)
    {
        if (!ObjectId.TryParse(id, out var unitId))
            throw new BadRequestException(HttpEnum.ErrorMessage.BadRequestInvalidFormat);
        var unitUpdated =
            await unitService.UpdateUnitInfo(unitId, request.Name, request.Title, request.ConfigObservationId);

        logger.LogInformation("Section info {sectionid} updated at UTC {DateTimeUtcNow}", unitId, DateTime.UtcNow);
        return Ok(unitUpdated);
    }

    /// <summary>
    /// Retrieves the dependency information for the specified unit, returning it as a DTO.
    /// </summary>
    /// <param name="id">The string representation of the unit's identifier used to look up the unit.</param>
    /// <returns>An <see cref="IActionResult"/> containing the unit dependency DTO on success.</returns>
    /// <exception cref="BadRequestException">Thrown when <paramref name="id"/> is not a valid format or when the dependency DTO result cannot be retrieved.</exception>
    /// <exception cref="NotFoundException">Thrown when no unit exists with the specified identifier.</exception>
    [HttpGet("{id}/dependency")]
    [AuthorizeRoles(PermissionEnum.RolesType.AuthSome)]
    [PermissionAuthorize("Source", "DisplayId")]
    public async Task<IActionResult> GetUnitDependency(string id)
    {
        if (!ObjectId.TryParse(id, out var unitId))
            throw new BadRequestException(HttpEnum.ErrorMessage.BadRequestInvalidFormat);
        var unit = await unitService.FindById(unitId) ??
                   throw new NotFoundException(HttpEnum.ErrorMessage.NotFoundResourceMissing);


        var result = await adminPanelService.GetUnitDependencyDto(unit) ??
                     throw new BadRequestException(HttpEnum.ErrorMessage.InternalServerErrorUnexpected);

        return Ok(result);
    }

    #endregion
}