using Microsoft.Extensions.Logging;
using QYZH.InteractiveMagazine.IService;
using QYZH.InteractiveMagazine.Models.Common;
using QYZH.InteractiveMagazine.Models.Dto;
using QYZH.InteractiveMagazine.Models.Entity;
using QYZH.InteractiveMagazine.Models.Enum;
using QYZH.InteractiveMagazine.Repository;
using SqlSugar;

namespace QYZH.InteractiveMagazine.Service;

/// <summary>
/// 后台权限管理服务实现
/// </summary>
public class AdminPermissionService(
    BaseRepository<AdminRole> adminRoleRepository,
    BaseRepository<AdminMenu> adminMenuRepository,
    BaseRepository<AdminRoleMenu> adminRoleMenuRepository,
    BaseRepository<AdminUserRole> adminUserRoleRepository,
    BaseRepository<AdminUser> adminUserRepository,
    ILogger<AdminPermissionService> logger) : BaseRepository<AdminRole>, IAdminPermissionService
{
    /// <summary>
    /// 创建菜单
    /// </summary>
    public async Task<AdminMenuOutput> CreateMenuAsync(AdminMenuInput input)
    {
        await ValidateMenuInputAsync(input);

        var menu = new AdminMenu
        {
            ParentId = input.ParentId,
            Name = input.Name.Trim(),
            Code = input.Code.Trim(),
            Path = input.Path?.Trim(),
            Component = input.Component?.Trim(),
            Icon = input.Icon?.Trim(),
            Sort = input.Sort,
            IsVisible = input.IsVisible,
            Status = input.Status,
            CreatedBy = "System",
            CreatedAt = DateTime.Now,
            UpdatedBy = "System",
            UpdatedAt = DateTime.Now,
            IsDeleted = false
        };

        var result = await adminMenuRepository.InsertAsync(menu);
        BusinessException.ThrowIf(!result, "创建菜单失败", ResultCode.GLOBAL_ERROR);

        logger.LogInformation("创建后台菜单成功：{Code}，ID：{Id}", menu.Code, menu.Id);
        return ToMenuOutput(menu);
    }

    /// <summary>
    /// 更新菜单
    /// </summary>
    public async Task<AdminMenuOutput> UpdateMenuAsync(long id, AdminMenuInput input)
    {
        var menu = await adminMenuRepository.GetByIdAsync(id);
        BusinessException.ThrowIf(menu == null, "菜单不存在", ResultCode.NOT_FOUND);
        BusinessException.ThrowIf(input.ParentId == id, "父级菜单不能选择自身", ResultCode.BAD_REQUEST);

        await ValidateMenuInputAsync(input, id);

        menu!.ParentId = input.ParentId;
        menu.Name = input.Name.Trim();
        menu.Code = input.Code.Trim();
        menu.Path = input.Path?.Trim();
        menu.Component = input.Component?.Trim();
        menu.Icon = input.Icon?.Trim();
        menu.Sort = input.Sort;
        menu.IsVisible = input.IsVisible;
        menu.Status = input.Status;
        menu.UpdatedBy = "System";
        menu.UpdatedAt = DateTime.Now;

        var result = await adminMenuRepository.UpdateAsync(menu);
        BusinessException.ThrowIf(!result, "更新菜单失败", ResultCode.GLOBAL_ERROR);

        logger.LogInformation("更新后台菜单成功：{Code}，ID：{Id}", menu.Code, menu.Id);
        return ToMenuOutput(menu);
    }

    /// <summary>
    /// 删除菜单
    /// </summary>
    public async Task DeleteMenuAsync(long id)
    {
        var menu = await adminMenuRepository.GetByIdAsync(id);
        BusinessException.ThrowIf(menu == null, "菜单不存在", ResultCode.NOT_FOUND);

        var hasChildren = await adminMenuRepository.Queryable().AnyAsync(x => x.ParentId == id && !x.IsDeleted);
        BusinessException.ThrowIf(hasChildren, "请先删除子菜单", ResultCode.CONFLICT);

        var usedByRole = await adminRoleMenuRepository.Queryable().AnyAsync(x => x.MenuId == id && !x.IsDeleted);
        BusinessException.ThrowIf(usedByRole, "菜单已被角色使用，不能删除", ResultCode.CONFLICT);

        var result = await adminMenuRepository.DeleteByIdAsync(id);
        BusinessException.ThrowIf(!result, "删除菜单失败", ResultCode.GLOBAL_ERROR);
    }

    /// <summary>
    /// 获取菜单树
    /// </summary>
    public async Task<List<AdminMenuOutput>> GetMenuTreeAsync(AdminMenuQueryInput input)
    {
        var menus = await adminMenuRepository.Queryable()
            .Where(x => !x.IsDeleted)
            .WhereIF(!string.IsNullOrWhiteSpace(input.Name), x => x.Name.Contains(input.Name!))
            .WhereIF(!string.IsNullOrWhiteSpace(input.Code), x => x.Code.Contains(input.Code!))
            .WhereIF(input.Status.HasValue, x => x.Status == input.Status!.Value)
            .OrderBy(x => x.Sort)
            .OrderBy(x => x.Id)
            .ToListAsync();

        return BuildMenuTree(menus);
    }

    /// <summary>
    /// 创建角色
    /// </summary>
    public async Task<AdminRoleOutput> CreateRoleAsync(AdminRoleInput input)
    {
        await ValidateRoleInputAsync(input);

        var role = new AdminRole
        {
            Name = input.Name.Trim(),
            Code = input.Code.Trim(),
            Remark = input.Remark?.Trim(),
            Status = input.Status,
            CreatedBy = "System",
            CreatedAt = DateTime.Now,
            UpdatedBy = "System",
            UpdatedAt = DateTime.Now,
            IsDeleted = false
        };

        await UseTranAsync(async () =>
        {
            var inserted = await adminRoleRepository.InsertAsync(role);
            BusinessException.ThrowIf(!inserted, "创建角色失败", ResultCode.GLOBAL_ERROR);
            await ReplaceRoleMenusAsync(role.Id, input.MenuIds);
        });

        logger.LogInformation("创建后台角色成功：{Code}，ID：{Id}", role.Code, role.Id);
        return await GetRoleByIdAsync(role.Id);
    }

    /// <summary>
    /// 更新角色
    /// </summary>
    public async Task<AdminRoleOutput> UpdateRoleAsync(long id, AdminRoleInput input)
    {
        var role = await adminRoleRepository.GetByIdAsync(id);
        BusinessException.ThrowIf(role == null, "角色不存在", ResultCode.NOT_FOUND);

        await ValidateRoleInputAsync(input, id);

        role!.Name = input.Name.Trim();
        role.Code = input.Code.Trim();
        role.Remark = input.Remark?.Trim();
        role.Status = input.Status;
        role.UpdatedBy = "System";
        role.UpdatedAt = DateTime.Now;

        await UseTranAsync(async () =>
        {
            var updated = await adminRoleRepository.UpdateAsync(role);
            BusinessException.ThrowIf(!updated, "更新角色失败", ResultCode.GLOBAL_ERROR);
            await ReplaceRoleMenusAsync(role.Id, input.MenuIds);
        });

        logger.LogInformation("更新后台角色成功：{Code}，ID：{Id}", role.Code, role.Id);
        return await GetRoleByIdAsync(role.Id);
    }

    /// <summary>
    /// 删除角色
    /// </summary>
    public async Task DeleteRoleAsync(long id)
    {
        var role = await adminRoleRepository.GetByIdAsync(id);
        BusinessException.ThrowIf(role == null, "角色不存在", ResultCode.NOT_FOUND);

        var usedByUser = await adminUserRoleRepository.Queryable().AnyAsync(x => x.RoleId == id && !x.IsDeleted);
        BusinessException.ThrowIf(usedByUser, "角色已分配给管理员，不能删除", ResultCode.CONFLICT);

        await UseTranAsync(async () =>
        {
            await adminRoleMenuRepository.Deleteable().Where(x => x.RoleId == id).ExecuteCommandAsync();
            var deleted = await adminRoleRepository.DeleteByIdAsync(id);
            BusinessException.ThrowIf(!deleted, "删除角色失败", ResultCode.GLOBAL_ERROR);
        });
    }

    /// <summary>
    /// 获取角色详情
    /// </summary>
    public async Task<AdminRoleOutput> GetRoleByIdAsync(long id)
    {
        var role = await adminRoleRepository.GetByIdAsync(id);
        BusinessException.ThrowIf(role == null, "角色不存在", ResultCode.NOT_FOUND);

        var menuIds = await adminRoleMenuRepository.Queryable()
            .Where(x => x.RoleId == id && !x.IsDeleted)
            .Select(x => x.MenuId)
            .ToListAsync();

        return ToRoleOutput(role!, menuIds);
    }

    /// <summary>
    /// 获取角色分页列表
    /// </summary>
    public async Task<PageListModel<AdminRoleOutput>> GetRoleListAsync(AdminRoleQueryInput input)
    {
        RefAsync<int> totalNumber = 0;
        var roles = await adminRoleRepository.Queryable()
            .Where(x => !x.IsDeleted)
            .WhereIF(!string.IsNullOrWhiteSpace(input.Name), x => x.Name.Contains(input.Name!))
            .WhereIF(!string.IsNullOrWhiteSpace(input.Code), x => x.Code.Contains(input.Code!))
            .WhereIF(input.Status.HasValue, x => x.Status == input.Status!.Value)
            .OrderByDescending(x => x.CreatedAt)
            .ToPageListAsync(input.PageIndex, input.PageSize, totalNumber);

        var roleIds = roles.Select(x => x.Id).ToList();
        var roleMenus = roleIds.Count == 0
            ? []
            : await adminRoleMenuRepository.Queryable()
                .Where(x => roleIds.Contains(x.RoleId) && !x.IsDeleted)
                .ToListAsync();

        var outputs = roles
            .Select(x => ToRoleOutput(x, roleMenus.Where(rm => rm.RoleId == x.Id).Select(rm => rm.MenuId).ToList()))
            .ToList();

        return new PageListModel<AdminRoleOutput>(outputs, input.PageIndex, input.PageSize, totalNumber);
    }

    /// <summary>
    /// 分配角色菜单
    /// </summary>
    public async Task AssignRoleMenusAsync(long roleId, AssignRoleMenusInput input)
    {
        var role = await adminRoleRepository.GetByIdAsync(roleId);
        BusinessException.ThrowIf(role == null, "角色不存在", ResultCode.NOT_FOUND);

        await ValidateMenuIdsAsync(input.MenuIds);
        await UseTranAsync(async () => await ReplaceRoleMenusAsync(roleId, input.MenuIds));
    }

    /// <summary>
    /// 分配管理员角色
    /// </summary>
    public async Task AssignAdminUserRolesAsync(long adminUserId, AssignAdminUserRolesInput input)
    {
        await ReplaceAdminUserRolesAsync(adminUserId, input.RoleIds);
    }

    /// <summary>
    /// 获取管理员菜单树
    /// </summary>
    public async Task<List<AdminMenuOutput>> GetAdminUserMenuTreeAsync(long adminUserId)
    {
        var menus = await GetAdminUserMenusAsync(adminUserId, true);
        return BuildMenuTree(menus);
    }

    /// <summary>
    /// 获取管理员角色
    /// </summary>
    public async Task<List<AdminRoleSimpleOutput>> GetAdminUserRolesAsync(long adminUserId)
    {
        var adminUser = await adminUserRepository.GetByIdAsync(adminUserId);
        BusinessException.ThrowIf(adminUser == null, "管理员不存在", ResultCode.NOT_FOUND);

        return await adminRoleRepository.Queryable()
            .InnerJoin<AdminUserRole>((role, userRole) => role.Id == userRole.RoleId)
            .Where((role, userRole) => userRole.AdminUserId == adminUserId && !role.IsDeleted && !userRole.IsDeleted)
            .Select((role, userRole) => new AdminRoleSimpleOutput
            {
                Id = role.Id,
                Name = role.Name,
                Code = role.Code
            })
            .ToListAsync();
    }

    /// <summary>
    /// 获取管理员权限编码
    /// </summary>
    public async Task<List<string>> GetAdminUserPermissionCodesAsync(long adminUserId)
    {
        var menus = await GetAdminUserMenusAsync(adminUserId, false);
        return menus.Select(x => x.Code).Where(x => !string.IsNullOrWhiteSpace(x)).Distinct().ToList();
    }

    private async Task ValidateMenuInputAsync(AdminMenuInput input, long? id = null)
    {
        BusinessException.ThrowIf(string.IsNullOrWhiteSpace(input.Name), "菜单名称不能为空", ResultCode.BAD_REQUEST);
        BusinessException.ThrowIf(string.IsNullOrWhiteSpace(input.Code), "权限编码不能为空", ResultCode.BAD_REQUEST);

        if (input.ParentId > 0)
        {
            var parentExists = await adminMenuRepository.Queryable().AnyAsync(x => x.Id == input.ParentId && !x.IsDeleted);
            BusinessException.ThrowIf(!parentExists, "父级菜单不存在", ResultCode.NOT_FOUND);
        }

        var code = input.Code.Trim();
        var codeExists = await adminMenuRepository.Queryable()
            .AnyAsync(x => x.Code == code && !x.IsDeleted && (!id.HasValue || x.Id != id.Value));
        BusinessException.ThrowIf(codeExists, "权限编码已存在", ResultCode.CONFLICT);
    }

    private async Task ValidateRoleInputAsync(AdminRoleInput input, long? id = null)
    {
        BusinessException.ThrowIf(string.IsNullOrWhiteSpace(input.Name), "角色名称不能为空", ResultCode.BAD_REQUEST);
        BusinessException.ThrowIf(string.IsNullOrWhiteSpace(input.Code), "角色编码不能为空", ResultCode.BAD_REQUEST);

        var code = input.Code.Trim();
        var codeExists = await adminRoleRepository.Queryable()
            .AnyAsync(x => x.Code == code && !x.IsDeleted && (!id.HasValue || x.Id != id.Value));
        BusinessException.ThrowIf(codeExists, "角色编码已存在", ResultCode.CONFLICT);

        await ValidateMenuIdsAsync(input.MenuIds);
    }

    private async Task ValidateMenuIdsAsync(List<long> menuIds)
    {
        var ids = menuIds.Distinct().ToList();
        if (ids.Count == 0)
        {
            return;
        }

        var existsCount = await adminMenuRepository.Queryable()
            .Where(x => ids.Contains(x.Id) && !x.IsDeleted)
            .CountAsync();
        BusinessException.ThrowIf(existsCount != ids.Count, "包含不存在的菜单", ResultCode.BAD_REQUEST);
    }

    private async Task ValidateRoleIdsAsync(List<long> roleIds)
    {
        var ids = roleIds.Distinct().ToList();
        if (ids.Count == 0)
        {
            return;
        }

        var existsCount = await adminRoleRepository.Queryable()
            .Where(x => ids.Contains(x.Id) && !x.IsDeleted)
            .CountAsync();
        BusinessException.ThrowIf(existsCount != ids.Count, "包含不存在的角色", ResultCode.BAD_REQUEST);
    }

    private async Task ReplaceRoleMenusAsync(long roleId, List<long> menuIds)
    {
        await adminRoleMenuRepository.Deleteable().Where(x => x.RoleId == roleId).ExecuteCommandAsync();

        var now = DateTime.Now;
        var items = menuIds.Distinct().Select(menuId => new AdminRoleMenu
        {
            RoleId = roleId,
            MenuId = menuId,
            Status = (int)DefaultStatusEnum.Active,
            CreatedBy = "System",
            CreatedAt = now,
            UpdatedBy = "System",
            UpdatedAt = now,
            IsDeleted = false
        }).ToList();

        if (items.Count > 0)
        {
            await adminRoleMenuRepository.Context.Insertable(items).ExecuteCommandAsync();
        }
    }

    private async Task ReplaceAdminUserRolesAsync(long adminUserId, List<long> roleIds)
    {
        var adminUser = await adminUserRepository.GetByIdAsync(adminUserId);
        BusinessException.ThrowIf(adminUser == null, "管理员不存在", ResultCode.NOT_FOUND);

        await ValidateRoleIdsAsync(roleIds);

        await UseTranAsync(async () =>
        {
            await adminUserRoleRepository.Deleteable().Where(x => x.AdminUserId == adminUserId).ExecuteCommandAsync();

            var now = DateTime.Now;
            var items = roleIds.Distinct().Select(roleId => new AdminUserRole
            {
                AdminUserId = adminUserId,
                RoleId = roleId,
                Status = (int)DefaultStatusEnum.Active,
                CreatedBy = "System",
                CreatedAt = now,
                UpdatedBy = "System",
                UpdatedAt = now,
                IsDeleted = false
            }).ToList();

            if (items.Count > 0)
            {
                await adminUserRoleRepository.Context.Insertable(items).ExecuteCommandAsync();
            }
        });
    }

    private async Task<List<AdminMenu>> GetAdminUserMenusAsync(long adminUserId, bool visibleOnly)
    {
        var adminUser = await adminUserRepository.GetByIdAsync(adminUserId);
        BusinessException.ThrowIf(adminUser == null, "管理员不存在", ResultCode.NOT_FOUND);

        if (adminUser!.Type == AdminUserTypeEnum.SuperAdmin)
        {
            return await adminMenuRepository.Queryable()
                .Where(x => !x.IsDeleted && x.Status == (int)DefaultStatusEnum.Active)
                .WhereIF(visibleOnly, x => x.IsVisible)
                .OrderBy(x => x.Sort)
                .OrderBy(x => x.Id)
                .ToListAsync();
        }

        var menus = await adminMenuRepository.Queryable()
            .InnerJoin<AdminRoleMenu>((menu, roleMenu) => menu.Id == roleMenu.MenuId)
            .InnerJoin<AdminRole>((menu, roleMenu, role) => roleMenu.RoleId == role.Id)
            .InnerJoin<AdminUserRole>((menu, roleMenu, role, userRole) => role.Id == userRole.RoleId)
            .Where((menu, roleMenu, role, userRole) =>
                userRole.AdminUserId == adminUserId
                && !menu.IsDeleted
                && !roleMenu.IsDeleted
                && !role.IsDeleted
                && !userRole.IsDeleted
                && menu.Status == (int)DefaultStatusEnum.Active
                && role.Status == (int)DefaultStatusEnum.Active)
            .WhereIF(visibleOnly, (menu, roleMenu, role, userRole) => menu.IsVisible)
            .OrderBy((menu, roleMenu, role, userRole) => menu.Sort)
            .OrderBy((menu, roleMenu, role, userRole) => menu.Id)
            .Select((menu, roleMenu, role, userRole) => menu)
            .ToListAsync();

        return menus.DistinctBy(x => x.Id).OrderBy(x => x.Sort).ThenBy(x => x.Id).ToList();
    }

    private static List<AdminMenuOutput> BuildMenuTree(List<AdminMenu> menus)
    {
        var outputs = menus.Select(ToMenuOutput).ToList();
        var lookup = outputs.ToLookup(x => x.ParentId);

        foreach (var item in outputs)
        {
            item.Children = lookup[item.Id].OrderBy(x => x.Sort).ThenBy(x => x.Id).ToList();
        }

        return outputs
            .Where(x => x.ParentId == 0 || outputs.All(item => item.Id != x.ParentId))
            .OrderBy(x => x.Sort)
            .ThenBy(x => x.Id)
            .ToList();
    }

    private static AdminMenuOutput ToMenuOutput(AdminMenu menu)
    {
        return new AdminMenuOutput
        {
            Id = menu.Id,
            ParentId = menu.ParentId,
            Name = menu.Name,
            Code = menu.Code,
            Path = menu.Path,
            Component = menu.Component,
            Icon = menu.Icon,
            Sort = menu.Sort,
            IsVisible = menu.IsVisible,
            Status = menu.Status
        };
    }

    private static AdminRoleOutput ToRoleOutput(AdminRole role, List<long> menuIds)
    {
        return new AdminRoleOutput
        {
            Id = role.Id,
            Name = role.Name,
            Code = role.Code,
            Remark = role.Remark,
            Status = role.Status,
            MenuIds = menuIds,
            CreatedAt = role.CreatedAt
        };
    }
}
