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 adas_core.Logging;
using Microsoft.AspNetCore.Mvc;
using MongoDB.Bson;

namespace adas_core.Controllers;

[Route("config-observations")]
[ApiController]
public class ConfigObservationController : ControllerBase
{
    private readonly IConfigObservationService _configObservationService;
    private readonly IHistoricalConfigChangesService _historicalConfigChangesService;

    public ConfigObservationController(IConfigObservationService configObservationService,
        IHistoricalConfigChangesService historicalConfigChangesService)
    {
        LogExecutionContext.TrySetTraceIdentifier(Guid.NewGuid().ToString(), true);

        _configObservationService = configObservationService;
        _historicalConfigChangesService = historicalConfigChangesService;
    }

    /// <summary>
    /// Retrieves all active configuration observations. Throws a not-found exception when no configurations are available.
    /// </summary>
    /// <returns>An <see cref="IActionResult"/> containing the active configuration observations.</returns>
    /// <exception cref="NotFoundException">Thrown when no configuration observations are returned by the service.</exception>
    [HttpGet]
    [AuthorizeRoles(PermissionEnum.RolesType.AuthSome)]
    [PermissionAuthorize("Source", "DisplayId")]
    public async Task<IActionResult> GetActiveConfigObservation()
    {
        var configObservation = await _configObservationService.GetAllConfigs() ??
                                throw new NotFoundException(HttpEnum.ErrorMessage.NotFoundResourceMissing);
        return Ok(configObservation);
    }

    /// <summary>
    ///     GetByCodeSysAndCode compact configs
    /// </summary>
    /// <returns></returns>
    [HttpGet("compact")]
    [AuthorizeRoles(PermissionEnum.RolesType.AuthSome)]
    [PermissionAuthorize("Source", "DisplayId")]
    public async Task<IActionResult> GetAllCompact()
    {
        var configObservation = await _configObservationService.GetAllCompact();
        return Ok(configObservation);
    }


    /// <summary>
    ///     Get config by id
    /// </summary>
    /// <param name="id"></param>
    /// <returns></returns>
    [HttpGet("{id}")]
    [AuthorizeRoles(PermissionEnum.RolesType.AuthSome)]
    [PermissionAuthorize("Source", "DisplayId")]
    public async Task<IActionResult> FindConfigObservationById(string id)
    {
        if (!ObjectId.TryParse(id, out var objectId))
            throw new InvalidFormatException(HttpEnum.ErrorMessage.BadRequestInvalidFormat);
        var configObservation = await _configObservationService.GetConfigById(objectId) ??
                                throw new NotFoundException(HttpEnum.ErrorMessage.NotFoundResourceMissing);
        return Ok(configObservation);
    }

    #region ADMPanel

    /// <summary>
    ///     Get a list of paginated items
    /// </summary>
    /// <returns></returns>
    [HttpPost("paginated-items")]
    [AuthorizeRoles(PermissionEnum.RolesType.AuthSome)]
    [PermissionAuthorize("Source", "DisplayId")]
    public async Task<IActionResult> GetPaginatedItems([FromBody] PaginationFilter filter)
    {
        var configObservation = await _configObservationService.GetPaginatedItems(filter);

        return Ok(configObservation);
    }


    /// <summary>
    ///     Post new config observation
    /// </summary>
    /// <param name="configObservation"></param>
    /// <returns></returns>
    [HttpPost]
    [AuthorizeRoles(PermissionEnum.RolesType.AuthSome)]
    [PermissionAuthorize("Source", "DisplayId")]
    public async Task<IActionResult> CreateConfigObservation([FromBody] ConfigObservation configObservation)
    {
        var newConfig = await _configObservationService.CreateConfig(configObservation);
        if (newConfig == null)
            return Conflict($"config with id: {configObservation.Id} already exists");

        await LogHistoricalConfigChanged(configObservation, null);

        return Ok(newConfig);
    }

    /// <summary>
    ///     Post new observation
    /// </summary>
    /// <param name="configObservationItem"></param>
    /// <returns></returns>
    [HttpPost("item")]
    [AuthorizeRoles(PermissionEnum.RolesType.AuthSome)]
    [PermissionAuthorize("Source", "DisplayId")]
    public async Task<IActionResult> CreateObservation([FromBody] ConfigObservation configObservationItem)
    {
        var newObservation = await _configObservationService.CreateConfig(configObservationItem)
                             ?? throw new ConflictException(HttpEnum.ErrorMessage.ConflictCreationFailed);

        return Ok(newObservation);
    }



    /// <summary>
    ///     Update config observation item
    /// </summary>
    /// <param name="configId"></param>
    /// <param name="configObservationItem"></param>
    /// <returns></returns>
    [HttpPut("{configId}")]
    [AuthorizeRoles(PermissionEnum.RolesType.AuthSome)]
    [PermissionAuthorize("Source", "DisplayId")]
    public async Task<IActionResult> UpdateConfigObservationItem(string configId,
        [FromBody] ConfigObservation configObservationItem)
    {
        if (!ObjectId.TryParse(configId, out var objectId))
            throw new BadRequestException(HttpEnum.ErrorMessage.BadRequestMissingParameters);

        var oldConfigObservation = await _configObservationService.GetConfigById(objectId) ??
                                   throw new NotFoundException(HttpEnum.ErrorMessage.NotFoundResourceMissing);
        var configObservation = await _configObservationService.UpdateConfig(configObservationItem);
        await LogHistoricalConfigChanged(configObservation, oldConfigObservation);
        return Ok(true);
    }

    /// <summary>
    ///     Get by name config observation item list
    /// </summary>
    /// <param name="itemName"></param>
    /// <returns></returns>
    [HttpGet("itemName/{itemName}")]
    [AuthorizeRoles(PermissionEnum.RolesType.AuthSome)]
    [PermissionAuthorize("Source", "DisplayId")]
    public async Task<IActionResult> GetConfigObservationItemByName(string itemName)
    {
        var oldConfigObservation = await _configObservationService.GetConfigObservationItemsByName(itemName);
        return Ok(oldConfigObservation);
    }

    /// <summary>
    ///     GetByCodeSysAndCode config observation item
    /// </summary>
    /// <param name="data"></param>
    /// <returns></returns>
    [HttpGet("item/by-code-sys-and-code/{data}")]
    [AuthorizeRoles(PermissionEnum.RolesType.AuthSome)]
    [PermissionAuthorize("Source", "DisplayId")]
    public async Task<IActionResult> GetSingleConfigObservationItem(string data)
    {
        var parts = data.Split('-');
        var code = parts.Length > 0 ? parts[0] : string.Empty;
        var codingSystem = parts.Length > 1 ? parts[1] : string.Empty;
        var name = parts.Length > 2 ? parts[2] : string.Empty;
        var originalName = parts.Length > 3 ? parts[3] : string.Empty;
        var item = await _configObservationService.GetSingleConfigObservationItem(code, codingSystem, name,
            originalName);
        return Ok(item);
    }


    /// <summary>
    /// Deletes a single configuration observation item.
    /// </summary>
    /// <param name="configObservationItem">The configuration observation item to delete, provided in the request body.</param>
    /// <returns>An <see cref="IActionResult"/> containing the result of the deletion operation.</returns>
    /// <exception cref="BadRequestException">Thrown when the <paramref name="configObservationItem"/> is null, indicating missing required parameters.</exception>
    [HttpDelete("delete/item")]
    [AuthorizeRoles(PermissionEnum.RolesType.AuthSome)]
    [PermissionAuthorize("Source", "DisplayId")]
    public async Task<IActionResult> DeleteSingleConfigObservationItem(
            [FromBody] ConfigObservation configObservationItem)
    {
        if (configObservationItem == null)
            throw new BadRequestException(HttpEnum.ErrorMessage.BadRequestMissingParameters);
        var item = await _configObservationService.DeleteSingleConfigObservationItem(configObservationItem);
        return Ok(item);
    }

    /// <summary>
    ///     GetByCodeSysAndCode config all observation items by name
    /// </summary>
    /// <param name="itemName"></param>
    /// <returns></returns>
    [HttpGet("{itemName}/name")]
    [AuthorizeRoles(PermissionEnum.RolesType.AuthSome)]
    [PermissionAuthorize("Source", "DisplayId")]
    public async Task<IActionResult> GetConfigObservationItemsByName(string itemName)
    {
        if (itemName.Length < 2) throw new BadRequestException(HttpEnum.ErrorMessage.BadRequestIncorrectLength);
        var items = await _configObservationService.GetConfigObservationItemsByName(itemName);
        return Ok(items);
    }

    /// <summary>
    /// Retrieves the list of configuration names available for observations, restricted to users with the required role and source-level display permission.
    /// </summary>
    /// <returns>An <see cref="IActionResult"/> containing the configuration names returned by the observation service wrapped in an HTTP 200 OK response.</returns>
    [HttpGet("get-name-obs")]
    [AuthorizeRoles(PermissionEnum.RolesType.AuthSome)]
    [PermissionAuthorize("Source", "DisplayId")]
    public async Task<IActionResult> GetConfigNames()
    {
        var items = await _configObservationService.GetConfigNames();
        return Ok(items);
    }

    /// <summary>
    /// Logs a historical configuration change for an observation configuration, capturing the user identity (falling back to the remote IP address when the user is not authenticated) along with the new and previous configuration states serialized as JSON.
    /// </summary>
    /// <param name="newConfigObservation">The new configuration observation to log, or <c>null</c> to indicate the configuration was removed.</param>
    /// <param name="oldConfigObservation">The previous configuration observation to log, or <c>null</c> to indicate no prior configuration existed.</param>
    private async Task LogHistoricalConfigChanged(ConfigObservation? newConfigObservation,
            ConfigObservation? oldConfigObservation)
    {
        var user = HttpContext.User.Identity?.Name ?? HttpContext.Connection.RemoteIpAddress?.ToString();
        await _historicalConfigChangesService.LogConfigChanged(user!, DisplayConfigEnums.ConfigTypes.ObservationCfg,
            newConfigObservation?.ToJson() ?? string.Empty, oldConfigObservation?.ToJson() ?? string.Empty);
    }

    #endregion
}