using adas_core.Application.Exceptions;
using adas_core.Application.Repositories.Interfaces;
using adas_core.Application.Services.Interfaces;
using adas_core.Domain.Enums;
using adas_core.Domain.Models.AppSettings;
using adas_core.Domain.Models.MongoModels;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using MongoDB.Bson;

namespace adas_core.Application.Services;

public class PermissionService(
    IOptions<PermissionSettings> permissionsConfig,
    ILogger<PermissionService> logger,
    Lazy<IDisplayService> displayService,
    Lazy<IUserRepository> userRepository,
    Lazy<IAuthorityRepository> authorityRepository)
    : IPermissionService
{
    private readonly ILogger<PermissionService> _logger = logger;
    private readonly PermissionSettings _permissionsConfig = permissionsConfig.Value;

    public async Task<DisplayPermissionTypes> GetPermissionsForDisplay(Display display, User user)
    {
        var authorities = user.Authorization;
        if (authorities == null)
        {
            var a = await userRepository.Value.GetByUserAndAuthoritesName(user.UserName);
            authorities = a?.Authorization;
            user.Authorization = authorities;
        }

        if (authorities == null) throw new ForbbidenException(HttpEnum.ErrorMessage.ForbiddenNoPermission);

        foreach (var auth in authorities)
            if (display.Id.ToString().Equals(auth.DisplayId) || display.UnitId.ToString().Equals(auth.UnitId))
            {
                _logger.LogInformation("User {user} has role {role} for display {displayId} or unit {unitId}",
                    user.UserName, auth.Rol, auth.DisplayId, auth.UnitId);

                //var unitPerms = await GetPermissionsForUnit(display.UnitId.ToString(), user);
                switch ((PermissionEnum.RolesType?)Enum.Parse(typeof(PermissionEnum.RolesType),
                            auth.Rol ?? string.Empty))
                {
                    case PermissionEnum.RolesType.AuthGuest:
                        // return (await CheckPermissions(_permissionsConfig.Guest.Display, display.UnitId.ToString()));
                        return _permissionsConfig.Guest.Display;
                    case PermissionEnum.RolesType.AuthAdmin:
                        // return await CheckPermissions(_permissionsConfig.Admin.Display, display.UnitId.ToString());
                        return _permissionsConfig.Admin.Display;
                    case PermissionEnum.RolesType.AuthDeveloper:
                        // return await CheckPermissions(_permissionsConfig.Developer.Display, display.UnitId.ToString());
                        return _permissionsConfig.Developer.Display;
                    case PermissionEnum.RolesType.AuthDoctorchief:
                        // return await CheckPermissions(_permissionsConfig.DoctorChief.Display, display.UnitId.ToString());
                        return _permissionsConfig.DoctorChief.Display;
                    case PermissionEnum.RolesType.AuthDoctor:
                        // return await CheckPermissions(_permissionsConfig.Doctor.Display, display.UnitId.ToString());
                        return _permissionsConfig.Doctor.Display;
                    case PermissionEnum.RolesType.AuthNursesupervisor:
                        // return await CheckPermissions(_permissionsConfig.NursingSupervisor.Display, display.UnitId.ToString());
                        return _permissionsConfig.NursingSupervisor.Display;
                    case PermissionEnum.RolesType.AuthNurse:
                        // return await CheckPermissions(_permissionsConfig.Nurse.Display, display.UnitId.ToString());
                        return _permissionsConfig.Nurse.Display;
                }
            }

        //return _permissionsConfig.NoPermissions.Display;
        throw new ForbbidenException(HttpEnum.ErrorMessage.ForbiddenNoPermission);
    }

    public Task<DisplayPermissionTypes> GetPermissionsForUnit(string unitId, User user)
    {
        var authorities = user.Authorization;
        if (authorities == null) throw new ForbbidenException(HttpEnum.ErrorMessage.ForbiddenNoPermission);
        foreach (var auth in authorities)
            if (auth.UnitId != null && auth.UnitId.Equals(unitId))
            {
                _logger.LogInformation("User {user} has role {role} for unit {unitId}", user.UserName, auth.Rol,
                    auth.UnitId);

                switch ((PermissionEnum.RolesType?)Enum.Parse(typeof(PermissionEnum.RolesType),
                            auth.Rol ?? string.Empty))
                {
                    case PermissionEnum.RolesType.AuthGuest:
                        // return await CheckPermissions(_permissionsConfig.Guest.Unit, auth.UnitId);
                        return Task.FromResult(_permissionsConfig.Guest.Unit);
                    case PermissionEnum.RolesType.AuthAdmin:
                        // return await CheckPermissions(_permissionsConfig.Admin.Unit, auth.UnitId);
                        return Task.FromResult(_permissionsConfig.Admin.Unit);
                    case PermissionEnum.RolesType.AuthDeveloper:
                        // return await CheckPermissions(_permissionsConfig.Developer.Unit, auth.UnitId);
                        return Task.FromResult(_permissionsConfig.Developer.Unit);
                    case PermissionEnum.RolesType.AuthDoctorchief:
                        // return await CheckPermissions(_permissionsConfig.DoctorChief.Unit, auth.UnitId);
                        return Task.FromResult(_permissionsConfig.DoctorChief.Unit);
                    case PermissionEnum.RolesType.AuthDoctor:
                        // return await CheckPermissions(_permissionsConfig.Doctor.Unit, auth.UnitId);
                        return Task.FromResult(_permissionsConfig.Doctor.Unit);
                    case PermissionEnum.RolesType.AuthNursesupervisor:
                        // return await CheckPermissions(_permissionsConfig.NursingSupervisor.Unit, auth.UnitId);
                        return Task.FromResult(_permissionsConfig.NursingSupervisor.Unit);
                    case PermissionEnum.RolesType.AuthNurse:
                        // return await CheckPermissions(_permissionsConfig.Nurse.Unit, auth.UnitId);
                        return Task.FromResult(_permissionsConfig.Nurse.Unit);
                }
            }

        throw new ForbbidenException(HttpEnum.ErrorMessage.ForbiddenNoPermission);
    }

/*
        private async Task<DisplayPermissionTypes?> CheckPermissions(DisplayPermissionTypes guestUnit, string authUnitId)
        {
            var isParsed = ObjectId.TryParse(authUnitId, out var unitId);
            if(isParsed)
            {
               var unit = await uniService.Value.FindById(unitId);
               var unitConfiguration = unit?.Configuration;
               if (unitConfiguration != null)
               {
                   guestUnit.Admissions.Execute = unitConfiguration.ManualAdmit;

                   guestUnit.Discharges.Execute = unitConfiguration.ManualDischarge;

                   guestUnit.DemographicData.Create = unitConfiguration.ManualEdit;
                   guestUnit.DemographicData.Delete = unitConfiguration.ManualEdit;
                   guestUnit.DemographicData.Update = unitConfiguration.ManualEdit;
                   guestUnit.DemographicData.Execute = unitConfiguration.ManualEdit;
               }
            }

            _logger.LogInformation("Permissions for unit {unitId}: {@permissions}", authUnitId, guestUnit);

            return guestUnit;
        }
*/

    public async Task<PanelPermissionTypes> GetPermissionsForPanel(string user)
    {
        var userFound = await userRepository.Value.GetByUserName(user);
        if(userFound == null) throw new ForbbidenException(HttpEnum.ErrorMessage.ForbiddenNoPermission);
        userFound.Authorization = await authorityRepository.Value.GetUserAuthorities(userFound.Id);
        return GetPermissionsForPanel(userFound);
    }
    public PanelPermissionTypes GetPermissionsForPanel(User user)
    {
        var authorities = user.Authorization;
        if (authorities == null) throw new ForbbidenException(HttpEnum.ErrorMessage.ForbiddenNoPermission);

        foreach (var auth in authorities)
            if (auth.PanelAuthorization)
            {
                _logger.LogInformation("User {user} has role {role} for panel", user.UserName, auth.Rol);

                switch ((PermissionEnum.RolesType?)Enum.Parse(typeof(PermissionEnum.RolesType),
                            auth.Rol ?? string.Empty))
                {
                    case PermissionEnum.RolesType.AuthGuest:
                        return _permissionsConfig.Guest.Panel;
                    case PermissionEnum.RolesType.AuthAdmin:
                        return _permissionsConfig.Admin.Panel;
                    case PermissionEnum.RolesType.AuthDeveloper:
                        return _permissionsConfig.Developer.Panel;
                    case PermissionEnum.RolesType.AuthDoctorchief:
                        return _permissionsConfig.DoctorChief.Panel;
                    case PermissionEnum.RolesType.AuthDoctor:
                        return _permissionsConfig.Doctor.Panel;
                    case PermissionEnum.RolesType.AuthNursesupervisor:
                        return _permissionsConfig.NursingSupervisor.Panel;
                    case PermissionEnum.RolesType.AuthNurse:
                        return _permissionsConfig.Nurse.Panel;
                }
            }

        throw new ForbbidenException(HttpEnum.ErrorMessage.ForbiddenNoPermission);
    }

    public async Task<bool> HasAccessToDisplay(string username, PermissionEnum.RolesType userRole,
        PermissionEnum.SourcePermissionsEnum source, string displayId)
    {
        if (!ObjectId.TryParse(displayId, out var displayObjectId)) return false;

        var display = await displayService.Value.GetById(displayObjectId);
        var user = await userRepository.Value.GetByUserName(username);

        if (user == null || display == null) return false;
        var authorities = await authorityRepository.Value.GetUserAuthorities(user.Id);
        user.Authorization = authorities;

        return SearchRoleInDisplay(display, authorities, userRole);
    }

    public async Task<bool> HasAccessToUnit(string username, PermissionEnum.RolesType userRole,
        PermissionEnum.SourcePermissionsEnum source, string unitId)
    {
        var user = await userRepository.Value.GetByUserName(username);

        if (user == null) return false;
        var authorities = await authorityRepository.Value.GetUserAuthorities(user.Id);
        user.Authorization = authorities;

        return SearchRoleInUnit(unitId, authorities, userRole);
    }

    public async Task<bool> HasAccessToPanel(string username, PermissionEnum.RolesType userRole,
        PermissionEnum.SourcePermissionsEnum source)
    {
        var user = await userRepository.Value.GetByUserName(username);

        if (user == null) return false;
        var authorities = await authorityRepository.Value.GetUserAuthorities(user.Id);
        user.Authorization = authorities;

        return SearchRoleInPanel(authorities, userRole);
    }


    private static bool SearchRoleInDisplay(Display display, List<Authorization>? authorities,
        PermissionEnum.RolesType role)
    {
        if (authorities is not { Count: > 0 }) return false;

        return (
            from auth in authorities
            let authRole =
                (PermissionEnum.RolesType?)Enum.Parse(typeof(PermissionEnum.RolesType), auth.Rol ?? string.Empty)
            where display.Id.ToString().Equals(auth.DisplayId) || display.UnitId.ToString().Equals(auth.UnitId)
            let result = SearchRoleInUnit(display.UnitId.ToString(), authorities, role)
            where result || authRole == role
            select authRole
        ).Any();
    }

    private static bool SearchRoleInUnit(string unitId, List<Authorization>? authorities, PermissionEnum.RolesType role)
    {
        if (authorities == null)
            return false;
        foreach (var auth in authorities)
        {
            var authRole =
                (PermissionEnum.RolesType?)Enum.Parse(typeof(PermissionEnum.RolesType), auth.Rol ?? string.Empty);
            if (auth.UnitId != null && auth.UnitId.Equals(unitId) && authRole == role) return true;
        }

        return false;
    }

    private static bool SearchRoleInPanel(List<Authorization>? authorities, PermissionEnum.RolesType role)
    {
        if (authorities == null)
            return false;

        return (
            from auth in authorities
            let authRole =
                (PermissionEnum.RolesType?)Enum.Parse(typeof(PermissionEnum.RolesType), auth.Rol ?? string.Empty)
            where auth.PanelAuthorization && authRole == role
            select auth
        ).Any();
    }
}