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

namespace adas_core.Controllers;

[Route("pumps")]
public class PumpController : ControllerBase
{
    private readonly IHistoricalConfigChangesService _historicalConfigChangesService;
    private readonly IPumpService _pumpService;


    public PumpController(IPumpService pumpService, IHistoricalConfigChangesService historicalConfigChangesService)
    {
        LogExecutionContext.TrySetTraceIdentifier(Guid.NewGuid().ToString(), true);

        _pumpService = pumpService;
        _historicalConfigChangesService = historicalConfigChangesService;
    }

    /// <summary>
    /// Processes an incoming pump request by constructing an <see cref="ApiRequest"/> from the provided DTO and persisting it,
    /// using a synchronous save when the request indicates a wait-for-result, and an asynchronous save otherwise.
    /// </summary>
    /// <param name="pumpDto">The pump data transfer object received in the request body, containing the patient number, location, pump observations, message time, and wait result flag.</param>
    /// <returns>An <see cref="IActionResult"/> representing an HTTP 200 OK response.</returns>
    [HttpPost("request")]
    [AuthorizeRoles(PermissionEnum.RolesType.AuthSome)]
    [PermissionAuthorize("Source", "DisplayId")]
    public IActionResult ProcessRequest([FromBody] PumpDto pumpDto)
    {
        var request = new ApiRequest
        {
            TraceId = Guid.NewGuid().ToString(),
            Type = "AlarisPump",
            PatientNumber = pumpDto.PatientNumber,
            Location = pumpDto.Location,
            PumpObservations = pumpDto.PumpObservations,
            MessageTime = pumpDto.MessageTime,
            WaitResult = pumpDto.WaitResult
        };


        if (request.WaitResult) _pumpService.SaveRequest(request);

        else _pumpService.SaveRequestAsync(request);
        return Ok();
    }

    /// <summary>
    /// Retrieves all pump configuration records as an HTTP 200 OK response.
    /// Requires the caller to have the "AuthSome" role and "DisplayId" permission on the "Source" resource.
    /// </summary>
    /// <returns>An <see cref="IActionResult"/> containing the result of <c>_pumpService.GetAllPumpConfig()</c>.</returns>
    [HttpGet]
    [AuthorizeRoles(PermissionEnum.RolesType.AuthSome)]
    [PermissionAuthorize("Source", "DisplayId")]
    public async Task<IActionResult> GetAllPumpConfig()
    {
        return Ok(await _pumpService.GetAllPumpConfig());
    }

    /// <summary>
    /// Retrieves the pump configuration identified by the specified identifier, restricted to callers with the AuthSome role and DisplayId permission on the Source resource.
    /// </summary>
    /// <param name="id">The unique identifier of the pump configuration to retrieve.</param>
    /// <returns>An HTTP 200 response containing the pump configuration that matches the specified identifier.</returns>
    [HttpGet("{id}")]
    [AuthorizeRoles(PermissionEnum.RolesType.AuthSome)]
    [PermissionAuthorize("Source", "DisplayId")]
    public async Task<IActionResult> GetPumpConfigById(string id)
    {
        return Ok(await _pumpService.GetPumpConfigsById(id));
    }

    /// <summary>
    /// Updates an existing pump configuration and records the change in the historical log.
    /// Validates the incoming configuration payload, retrieving the previous configuration to enable historical tracking of changes.
    /// </summary>
    /// <param name="configPumps">The pump configuration data to update, provided in the request body.</param>
    /// <returns>An <see cref="IActionResult"/> containing the updated pump configuration wrapped in an Ok response.</returns>
    /// <exception cref="BadRequestException">Thrown when the <paramref name="configPumps"/> parameter is null.</exception>
    [HttpPut]
    [AuthorizeRoles(PermissionEnum.RolesType.AuthSome)]
    [PermissionAuthorize("Source", "DisplayId")]
    public async Task<IActionResult> UpdatePumpConfig([FromBody] ConfigPumps configPumps)
    {
        if (configPumps == null) throw new BadRequestException(HttpEnum.ErrorMessage.BadRequestMissingParameters);
        var oldConfig = await _pumpService.GetPumpConfigsById(configPumps.Id);

        var result = await _pumpService.UpdatePumpConfig(configPumps);
        await LogHistoricalConfigChanged(result, oldConfig);
        return Ok(result);
    }

    /// <summary>
    /// Inserts a new pump configuration record after validating the request payload.
    /// Requires the caller to have the appropriate role and source-related permission.
    /// </summary>
    /// <param name="config">The pump configuration data supplied in the request body to be inserted.</param>
    /// <returns>An <see cref="IActionResult"/> containing the outcome of the pump configuration insertion.</returns>
    /// <exception cref="BadRequestException">Thrown when <paramref name="config"/> is null, indicating that required parameters are missing.</exception>
    [HttpPost]
    [AuthorizeRoles(PermissionEnum.RolesType.AuthSome)]
    [PermissionAuthorize("Source", "DisplayId")]
    public async Task<IActionResult> InsertPumpConfig([FromBody] ConfigPumps config)
    {
        if (config == null) throw new BadRequestException(HttpEnum.ErrorMessage.BadRequestMissingParameters);
        var result = await _pumpService.InsertPumpConfig(config);
        await LogHistoricalConfigChanged(config, null);
        return Ok(result);
    }

    /// <summary>
    /// Deletes a pump configuration identified by the specified id, retrieving it first, removing it, and logging the historical change after the deletion.
    /// </summary>
    /// <param name="id">The identifier of the pump configuration to delete.</param>
    /// <returns>An <see cref="IActionResult"/> containing the result of the deletion operation.</returns>
    [HttpDelete("{id}")]
    [AuthorizeRoles(PermissionEnum.RolesType.AuthSome)]
    [PermissionAuthorize("Source", "DisplayId")]
    public async Task<IActionResult> DeletePumpConfig(string id)
    {
        var config = await _pumpService.GetPumpConfigsById(id);
        var result = await _pumpService.DeletePumpConfig(config!);

        await LogHistoricalConfigChanged(null, config);
        return Ok(result);
    }

    /// <summary>
    /// Retrieves a paginated list of pumps based on the supplied filter configuration.
    /// </summary>
    /// <param name="config">The pagination filter that defines the paging parameters for the pump query.</param>
    /// <returns>An <see cref="IActionResult"/> containing the paginated pump result.</returns>
    /// <exception cref="BadRequestException">Thrown when the <paramref name="config"/> parameter is null.</exception>
    [HttpPost("paginated")]
    [AuthorizeRoles(PermissionEnum.RolesType.AuthSome)]
    [PermissionAuthorize("Source", "DisplayId")]
    public async Task<IActionResult> PaginatedPump([FromBody] PaginationFilter config)
    {
        if (config == null) throw new BadRequestException(HttpEnum.ErrorMessage.BadRequestMissingParameters);
        var result = await _pumpService.GetPaginatedPump(config);
        return Ok(result);
    }


    /// <summary>
    /// Logs a historical change record for pump configuration updates by capturing the current user (or remote IP as fallback) and serializing the new and previous pump configurations to JSON.
    /// </summary>
    /// <param name="newConfigO">The new pump configuration to be logged, or <see langword="null"/> if not applicable.</param>
    /// <param name="oldConfig">The previous pump configuration to be logged, or <see langword="null"/> if not applicable.</param>
    private async Task LogHistoricalConfigChanged(ConfigPumps? newConfigO, ConfigPumps? oldConfig)
    {
        var user = HttpContext.User.Identity?.Name ?? HttpContext.Connection.RemoteIpAddress?.ToString();
        await _historicalConfigChangesService.LogConfigChanged(user!, DisplayConfigEnums.ConfigTypes.PumpCfg,
            newConfigO?.ToJson() ?? string.Empty, oldConfig?.ToJson() ?? string.Empty);
    }
}