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

namespace adas_core.Controllers;

[Route("master-lists")]
[ApiController]
public class MasterListController(
    IMasterListServiceFactory serviceFactory,
    ILogger<MasterListController> logger,
    IOptions<ListSettings> listSettings)
    : ControllerBase
{
    [HttpGet("list-settings")]
    [AuthorizeRoles(PermissionEnum.RolesType.AuthSome)]
    public IActionResult GetMasterListSettings()
    {
        return Ok(listSettings.Value);
    }

    [HttpGet("{typeName}")]
    [AuthorizeRoles(PermissionEnum.RolesType.AuthSome)]
    [PermissionAuthorize("Source", "DisplayId")]
    public async Task<IActionResult> GetMasterList(string typeName)
    {
        if (!Enum.TryParse(typeName, out MasterListType masterListType))
            throw new BadRequestException(HttpEnum.ErrorMessage.BadRequestIncorrectType);

        dynamic service = serviceFactory.GetService(masterListType);
        var result = await service.GetAllMasterList();
        return Ok(result);
    }


    [HttpGet("no-options")]
    [AuthorizeRoles(PermissionEnum.RolesType.AuthSome)]
    [PermissionAuthorize("Source", "DisplayId")]
    public async Task<IActionResult> GetAllMasterListWithoutOptions()
    {
        List<MasterListDto> listToReturn = [];
        foreach (MasterListType listType in Enum.GetValues(typeof(MasterListType)))
        {
            dynamic service = serviceFactory.GetService(listType);
            listToReturn.AddRange(await service.GetAllMasterListWithoutOptions());
        }

        return Ok(listToReturn);
    }

    [HttpPost("{typeName}/paginated-options")]
    [AuthorizeRoles(PermissionEnum.RolesType.AuthSome)]
    [PermissionAuthorize("Source", "DisplayId")]
    public async Task<IActionResult> GetMasterListWithPaginatedOptions(string typeName,
        [FromBody] PaginationFilter request)
    {
        if (!Enum.TryParse(typeName, out MasterListType masterListType))
            throw new BadRequestException(HttpEnum.ErrorMessage.BadRequestIncorrectType);

        dynamic service = serviceFactory.GetService(masterListType);
        var result = await service.GetAllMasterListWithPaginatedOptions(request);
        return Ok(result);
    }


    [HttpGet("{typeName}/count")]
    [AuthorizeRoles(PermissionEnum.RolesType.AuthSome)]
    [PermissionAuthorize("Source", "DisplayId")]
    public async Task<IActionResult> GetMasterListCount(string typeName)
    {
        if (!Enum.TryParse(typeName, out MasterListType masterListType))
            throw new BadRequestException(HttpEnum.ErrorMessage.BadRequestIncorrectType);

        dynamic service = serviceFactory.GetService(masterListType);
        var result = await service.GetAllMasterListCount();
        return Ok(result);
    }

    [HttpGet("count")]
    [AuthorizeRoles(PermissionEnum.RolesType.AuthSome)]
    [PermissionAuthorize("Source", "DisplayId")]
    public async Task<IActionResult> GetAllMasterListCounts()
    {
        var result = new Dictionary<MasterListType, int>();
        foreach (MasterListType listType in Enum.GetValues(typeof(MasterListType)))
        {
            dynamic service = serviceFactory.GetService(listType);
            result.Add(listType, await service.GetAllMasterListCount());
        }

        return Ok(result);
    }

    [HttpGet("{typeName}/{id}")]
    [AuthorizeRoles(PermissionEnum.RolesType.AuthSome)]
    [PermissionAuthorize("Source", "DisplayId")]
    public async Task<IActionResult> GetMasterListById(string typeName, string id)
    {
        if (!Enum.TryParse(typeName, out MasterListType masterListType))
            throw new BadRequestException(HttpEnum.ErrorMessage.BadRequestIncorrectType);

        if (!ObjectId.TryParse(id, out var masterListId))
            throw new BadRequestException(HttpEnum.ErrorMessage.BadRequestInvalidFormat);
        dynamic service = serviceFactory.GetService(masterListType);

        var result = await service.GetMasterListById(masterListId);
        return Ok(result);
    }

    [HttpPost("{typeName}/{id}/master-list-paginated-options")]
    [AuthorizeRoles(PermissionEnum.RolesType.AuthSome)]
    [PermissionAuthorize("Source", "DisplayId")]
    public async Task<IActionResult> GetMasterListByIdWithPaginatedOptions(string typeName, string id,
        [FromBody] PaginationFilter request)
    {
        if (!Enum.TryParse(typeName, out MasterListType masterListType))
            throw new BadRequestException(HttpEnum.ErrorMessage.BadRequestIncorrectType);

        if (!ObjectId.TryParse(id, out var masterListId))
            throw new BadRequestException(HttpEnum.ErrorMessage.BadRequestInvalidFormat);
        dynamic service = serviceFactory.GetService(masterListType);
        var result = await service.GetMasterListByIdWithPaginatedOptions(masterListId, request);
        return Ok(result);
    }

    [HttpPost("{typeName}/paginated-master-list-paginated-options")]
    [AuthorizeRoles(PermissionEnum.RolesType.AuthSome)]
    [PermissionAuthorize("Source", "DisplayId")]
    public async Task<IActionResult> GetPaginatedMasterListWithPaginatedOptions([FromBody] PaginationPairDto filters,
        string typeName)
    {
        logger.LogDebug("Start GetPaginatedMasterList");
        var start = DateTime.Now;

        if (!Enum.TryParse(typeName, out MasterListType masterListType))
            throw new BadRequestException(HttpEnum.ErrorMessage.BadRequestIncorrectType);

        dynamic service = serviceFactory.GetService(masterListType);

        var result = await service.GetPaginatedMasterListWithPaginatedOptions(filters.ListFilter, filters.OptionFilter);

        var end = DateTime.Now;
        logger.LogDebug("Finished GetPaginatedMasterList in {TotalSeconds:F1} seconds", (end - start).TotalSeconds);
        return Ok(result);
    }


    [HttpPost("{typeName}/{id}/paginated-options")]
    [AuthorizeRoles(PermissionEnum.RolesType.AuthSome)]
    [PermissionAuthorize("Source", "DisplayId")]
    public async Task<IActionResult> GetPaginatedOptions(string typeName, string id,
        [FromBody] PaginationFilter request)
    {
        logger.LogDebug("Start GetPaginatedOptions");
        var start = DateTime.Now;
        if (!Enum.TryParse(typeName, out MasterListType masterListType))
            throw new BadRequestException(HttpEnum.ErrorMessage.BadRequestIncorrectType);

        if (!ObjectId.TryParse(id, out var masterListId))
            throw new BadRequestException(HttpEnum.ErrorMessage.BadRequestInvalidFormat);
        dynamic service = serviceFactory.GetService(masterListType);

        var result = await service.GetPaginatedOptions(request, masterListId);

        var end = DateTime.Now;
        logger.LogDebug("Finished GetPaginatedOptions from list {id} in {TotalSeconds:F1} seconds", id,
            (end - start).TotalSeconds);

        return Ok(result);
    }

    [HttpGet("{typeName}/{id}/master-list-options-names")]
    [AuthorizeRoles(PermissionEnum.RolesType.AuthSome)]
    [PermissionAuthorize("Source", "DisplayId")]
    public async Task<IActionResult> GetMasterListOptionsNamesById(string typeName, string id)
    {
        if (!Enum.TryParse(typeName, out MasterListType masterListType))
            throw new BadRequestException(HttpEnum.ErrorMessage.BadRequestIncorrectType);

        if (!ObjectId.TryParse(id, out var masterListId))
            throw new BadRequestException(HttpEnum.ErrorMessage.BadRequestInvalidFormat);
        dynamic service = serviceFactory.GetService(masterListType);
        var result = await service.GetMasterListOptionsNamesById(masterListId);
        return Ok(result);
    }


    [HttpGet("{typeName}/{name}/name")]
    [AuthorizeRoles(PermissionEnum.RolesType.AuthSome)]
    [PermissionAuthorize("Source", "DisplayId")]
    public async Task<IActionResult> GetMasterListByName(string typeName, string name)
    {
        if (!Enum.TryParse(typeName, out MasterListType masterListType))
            throw new BadRequestException(HttpEnum.ErrorMessage.BadRequestIncorrectType);
        dynamic service = serviceFactory.GetService(masterListType);

        var result = await service.GetMasterListByName(name);

        return Ok(result);
    }

    [HttpGet("{typeName}/{listId}/related-options/{targetCategory}")]
    [AuthorizeRoles(PermissionEnum.RolesType.AuthSome)]
    [PermissionAuthorize("Source", "DisplayId")]
    public async Task<IActionResult> GetOptionTypeList(string typeName, string listId, string targetCategory)
    {
        if (!Enum.TryParse(typeName, out MasterListType masterListType1) ||
            !Enum.TryParse(targetCategory, out MasterListType masterListType2))
            throw new BadRequestException(HttpEnum.ErrorMessage.BadRequestIncorrectType);

        if (!ObjectId.TryParse(listId, out var masterListId))
            throw new BadRequestException(HttpEnum.ErrorMessage.BadRequestInvalidFormat);

        dynamic service1 = serviceFactory.GetService(masterListType1);

        ObjectId requestedListId = await service1.GetAssociatedList(masterListId, masterListType1, masterListType2);
        dynamic service2 = serviceFactory.GetService(masterListType2);

        MasterList options = await service2.GetMasterListById(requestedListId, LocaleEnum.Default);
        return Ok(options);
    }


    /// <summary>
    ///     Busca las opciones que contengan un texto de una lista determinada
    /// </summary>
    /// <param name="typeName">Tipo de Lista</param>
    /// <param name="id">id de la lista</param>
    /// <param name="searchOptions"></param>
    /// <returns></returns>
    [HttpPost("{typeName}/{id}/options")]
    [AuthorizeRoles(PermissionEnum.RolesType.AuthSome)]
    [PermissionAuthorize("Source", "DisplayId")]
    public async Task<IActionResult> GetMasterListByIdAndSearchOptions(string typeName, string id,
        [FromBody] FilterOptionListElement searchOptions)
    {
        logger.LogDebug("Start GetMasterListByIdAndSearchOptions");
        var start = DateTime.Now;
        if (!Enum.TryParse(typeName, out MasterListType masterListType))
            throw new BadRequestException(HttpEnum.ErrorMessage.BadRequestIncorrectType);

        if (!ObjectId.TryParse(id, out var masterListId))
            throw new BadRequestException(HttpEnum.ErrorMessage.BadRequestInvalidFormat);
        dynamic service = serviceFactory.GetService(masterListType);

        var result = await service.GetMasterListByIdAndSearchOptions(masterListId, searchOptions);

        var end = DateTime.Now;
        logger.LogDebug("Finished GetMasterListByIdAndSearchOptions in {TotalSeconds:F1} seconds",
            (end - start).TotalSeconds);

        return Ok(result);
    }

    [HttpGet("{typeName}/{textSearch}/text")]
    [AuthorizeRoles(PermissionEnum.RolesType.AuthSome)]
    [PermissionAuthorize("Source", "DisplayId")]
    public async Task<IActionResult> GetMasterListByTextSearch(string typeName, string? textSearch)
    {
        logger.LogDebug("Start GetMasterListByTextSearch");
        var start = DateTime.Now;

        if (!Enum.TryParse(typeName, out MasterListType masterListType))
            throw new BadRequestException(HttpEnum.ErrorMessage.BadRequestIncorrectType);

        if (textSearch is { Length: < 3 })
            throw new BadRequestException(HttpEnum.ErrorMessage.BadRequestIncorrectLength);
        dynamic service = serviceFactory.GetService(masterListType);

        var result = await service.GetMasterListByTextSearch(textSearch);
        var end = DateTime.Now;
        logger.LogDebug("Finished GetMasterListByTextSearch in {TotalSeconds:F1} seconds", (end - start).TotalSeconds);

        return Ok(result);
    }

    [HttpPost("{typeName}/paginated")]
    [AuthorizeRoles(PermissionEnum.RolesType.AuthSome)]
    [PermissionAuthorize("Source", "DisplayId")]
    public async Task<IActionResult> GetPaginatedMasterList([FromBody] PaginationFilter request, string typeName)
    {
        logger.LogDebug("Start GetPaginatedMasterList");
        var start = DateTime.Now;

        if (!Enum.TryParse(typeName, out MasterListType masterListType))
            throw new BadRequestException(HttpEnum.ErrorMessage.BadRequestIncorrectType);

        dynamic service = serviceFactory.GetService(masterListType);

        var result = await service.GetPaginatedMasterList(request);
        var end = DateTime.Now;
        logger.LogDebug("Finished GetPaginatedMasterList in {TotalSeconds:F1} seconds", (end - start).TotalSeconds);
        return Ok(result);
    }


    [HttpPost("{typeName}")]
    [AuthorizeRoles(PermissionEnum.RolesType.AuthSome)]
    [PermissionAuthorize("Source", "DisplayId")]
    public async Task<IActionResult> InsertMasterListFromPanel(string typeName, [FromBody] MasterList master)
    {
        if (master == null) throw new BadRequestException(HttpEnum.ErrorMessage.BadRequestMissingParameters);
        if (!Enum.TryParse(typeName, out MasterListType masterListType))
            throw new BadRequestException(HttpEnum.ErrorMessage.BadRequestIncorrectType);
        var result = await serviceFactory.InsertMasterList(masterListType, master);
        return Ok(result);
    }

    [HttpPut("{typeName}/{id}")]
    [AuthorizeRoles(PermissionEnum.RolesType.AuthSome)]
    [PermissionAuthorize("Source", "DisplayId")]
    public async Task<IActionResult> UpdateMasterList(string typeName, string id, [FromBody] MasterList master)
    {
        if (master == null) throw new BadRequestException(HttpEnum.ErrorMessage.BadRequestMissingParameters);

        if (!Enum.TryParse(typeName, out MasterListType masterListType))
            throw new BadRequestException(HttpEnum.ErrorMessage.BadRequestIncorrectType);

        if (!ObjectId.TryParse(id, out var masterListId))
            throw new BadRequestException(HttpEnum.ErrorMessage.BadRequestInvalidFormat);
        master.Id = masterListId;
        var result = await serviceFactory.UpdateMasterList(masterListType, master);
        return Ok(result);
    }

    [HttpPost("{typeName}/{id}/item")]
    [AuthorizeRoles(PermissionEnum.RolesType.AuthSome)]
    [PermissionAuthorize("Source", "DisplayId")]
    public async Task<IActionResult> AddOptionToMasterList(string typeName, string id,
        [FromBody] FilterOptionListElement opt)
    {
        var start = DateTime.Now;
        if (!Enum.TryParse(typeName, out MasterListType masterListType))
            throw new BadRequestException(HttpEnum.ErrorMessage.BadRequestIncorrectType);

        if (!ObjectId.TryParse(id, out var masterListId))
            throw new BadRequestException(HttpEnum.ErrorMessage.BadRequestInvalidFormat);

        dynamic service = serviceFactory.GetService(masterListType);

        var result = await service.AddOptionToMasterList(masterListId, opt);

        var end = DateTime.Now;
        logger.LogDebug("Finished AddOptionToMasterList in {TotalSeconds:F1} seconds", (end - start).TotalSeconds);
        return Ok(result);
    }

    [HttpPut("{locale}/{typeName}/{id}/item")]
    [AuthorizeRoles(PermissionEnum.RolesType.AuthSome)]
    [PermissionAuthorize("Source", "DisplayId")]
    public async Task<IActionResult> UpdateOptionFromMasterList(string locale, string typeName, string id,
        [FromBody] OptionList opt)
    {
        var start = DateTime.Now;
        if (!Enum.TryParse(typeName, out MasterListType masterListType))
            throw new BadRequestException(HttpEnum.ErrorMessage.BadRequestIncorrectType);
        if (!Enum.TryParse(locale, out LocaleEnum localeEnum))
            throw new BadRequestException(HttpEnum.ErrorMessage.BadRequestIncorrectType);

        if (!ObjectId.TryParse(id, out var masterListId))
            throw new BadRequestException(HttpEnum.ErrorMessage.BadRequestInvalidFormat);

        dynamic service = serviceFactory.GetService(masterListType);

        var result = await service.UpdateMasterListOption(masterListId, opt, typeName, localeEnum);

        var end = DateTime.Now;
        logger.LogDebug("Finished UpdateOptionToMasterList in {TotalSeconds:F1} seconds", (end - start).TotalSeconds);
        return result == null ? Conflict("Error while updating option in list") : (IActionResult)Ok(result);
    }

    [HttpPut("{typeName}/{id}/item")]
    [AuthorizeRoles(PermissionEnum.RolesType.AuthSome)]
    [PermissionAuthorize("Source", "DisplayId")]
    public async Task<IActionResult> UpdateOptionMasterListFromAdmPanel(string typeName, string id,
        [FromBody] OptionList opt)
    {
        var start = DateTime.Now;
        if (!Enum.TryParse(typeName, out MasterListType masterListType))
            throw new BadRequestException(HttpEnum.ErrorMessage.BadRequestIncorrectType);

        if (!ObjectId.TryParse(id, out var masterListId))
            throw new BadRequestException(HttpEnum.ErrorMessage.BadRequestInvalidFormat);

        dynamic service = serviceFactory.GetService(masterListType);

        var result = await service.UpdateMasterListOption(masterListId, opt, typeName);

        var end = DateTime.Now;
        logger.LogDebug("Finished UpdateOptionToMasterList in {TotalSeconds:F1} seconds", (end - start).TotalSeconds);
        return result == null ? Conflict("Error while updating option in list") : (IActionResult)Ok(result);
    }

    [HttpPut("{typeName}/{id}/details")]
    [AuthorizeRoles(PermissionEnum.RolesType.AuthSome)]
    [PermissionAuthorize("Source", "DisplayId")]
    public async Task<IActionResult> UpdateOptionDetailsFromMasterList(string typeName, string id,
        [FromBody] UpdateMasterListDetailsDto opt)
    {
        var start = DateTime.Now;
        if (!Enum.TryParse(typeName, out MasterListType masterListType))
            throw new BadRequestException(HttpEnum.ErrorMessage.BadRequestIncorrectType);

        if (!ObjectId.TryParse(id, out var masterListId))
            throw new BadRequestException(HttpEnum.ErrorMessage.BadRequestInvalidFormat);

        dynamic service = serviceFactory.GetService(masterListType);

        var result = await service.UpdateOptionDetailsToMasterList(masterListId, opt);

        var end = DateTime.Now;
        logger.LogDebug("Finished UpdateOptionToMasterList in {TotalSeconds:F1} seconds", (end - start).TotalSeconds);
        return result == null ? Conflict("Error while updating list details") : (IActionResult)Ok(result);
    }

    [HttpPut("{typeName}/{id}/name")]
    [AuthorizeRoles(PermissionEnum.RolesType.AuthSome)]
    [PermissionAuthorize("Source", "DisplayId")]
    public async Task<IActionResult> UpdateMasterListName(string typeName, string id,
        [FromBody] UpdateListNameAndDescriptionDto udpateListNameAndDescriptionDto)
    {
        var start = DateTime.Now;
        if (!Enum.TryParse(typeName, out MasterListType masterListType))
            throw new BadRequestException(HttpEnum.ErrorMessage.BadRequestIncorrectType);

        if (!ObjectId.TryParse(id, out var masterListId))
            throw new BadRequestException(HttpEnum.ErrorMessage.BadRequestInvalidFormat);

        if (udpateListNameAndDescriptionDto.Name == null) return BadRequest($"Invalid name: {typeName}.");
        dynamic service = serviceFactory.GetService(masterListType);

        var result = await service.UpdateMasterListName(masterListId, udpateListNameAndDescriptionDto.Name);

        var end = DateTime.Now;
        logger.LogDebug("Finished UpdateMasterListName in {TotalSeconds:F1} seconds", (end - start).TotalSeconds);
        return !result ? Conflict("Error while updating list name") : (IActionResult)Ok(result);
    }

    [HttpPut("{typeName}/{id}/description")]
    [AuthorizeRoles(PermissionEnum.RolesType.AuthSome)]
    [PermissionAuthorize("Source", "DisplayId")]
    public async Task<IActionResult> UpdateMasterListDescription(string typeName, string id,
        [FromBody] UpdateListNameAndDescriptionDto udpateListNameAndDescriptionDto)
    {
        var start = DateTime.Now;
        if (!Enum.TryParse(typeName, out MasterListType masterListType))
            throw new BadRequestException(HttpEnum.ErrorMessage.BadRequestIncorrectType);

        if (!ObjectId.TryParse(id, out var masterListId))
            throw new BadRequestException(HttpEnum.ErrorMessage.BadRequestInvalidFormat);

        if (udpateListNameAndDescriptionDto.Description == null) return BadRequest($"Description is null: {typeName}.");
        dynamic service = serviceFactory.GetService(masterListType);

        var result =
            await service.UpdateMasterListDescription(masterListId, udpateListNameAndDescriptionDto.Description);

        var end = DateTime.Now;
        logger.LogDebug("Finished UpdateMasterListDescription in {TotalSeconds:F1} seconds",
            (end - start).TotalSeconds);
        return !result ? Conflict("Error while updating list description") : (IActionResult)Ok(result);
    }


    [HttpDelete("{typeName}/{id}/item/{itemId}")]
    [AuthorizeRoles(PermissionEnum.RolesType.AuthSome)]
    [PermissionAuthorize("Source", "DisplayId")]
    public async Task<IActionResult> DeleteOptionFromMasterList(string typeName, string id, string itemId)
    {
        var start = DateTime.Now;
        if (!Enum.TryParse(typeName, out MasterListType masterListType))
            throw new BadRequestException(HttpEnum.ErrorMessage.BadRequestIncorrectType);

        if (!ObjectId.TryParse(id, out var masterListId))
            throw new BadRequestException(HttpEnum.ErrorMessage.BadRequestInvalidFormat);
        if (!ObjectId.TryParse(itemId, out var masterListItemId))
            throw new BadRequestException(HttpEnum.ErrorMessage.BadRequestInvalidFormat);

        dynamic service = serviceFactory.GetService(masterListType);

        var result = await service.DeleteMasterListOption(masterListId, masterListItemId, typeName);

        var end = DateTime.Now;
        logger.LogDebug("Finished DeleteOptionToMasterList in {TotalSeconds:F1} seconds", (end - start).TotalSeconds);
        if (result == null) throw new ConflictException(HttpEnum.ErrorMessage.ConflictDeleteFailed);
        return Ok(result);
    }

    [HttpDelete("{typeName}/{id}")]
    [AuthorizeRoles(PermissionEnum.RolesType.AuthSome)]
    [PermissionAuthorize("Source", "DisplayId")]
    public async Task<IActionResult> DeleteMasterList(string typeName, string id)
    {
        if (!Enum.TryParse(typeName, out MasterListType masterListType))
            throw new BadRequestException(HttpEnum.ErrorMessage.BadRequestIncorrectType);

        if (!ObjectId.TryParse(id, out var masterListId))
            throw new BadRequestException(HttpEnum.ErrorMessage.BadRequestInvalidFormat);

        dynamic service = serviceFactory.GetService(masterListType);

        await service.DeleteMasterListById(masterListId);
        return Ok();
    }
}