using MediatR;
using TakeoutSaaS.Application.Identity.Commands;
using TakeoutSaaS.Application.Identity.Contracts;
using TakeoutSaaS.Domain.Identity.Entities;
using TakeoutSaaS.Domain.Identity.Enums;
using TakeoutSaaS.Domain.Identity.Repositories;
using TakeoutSaaS.Shared.Abstractions.Constants;
using TakeoutSaaS.Shared.Abstractions.Exceptions;

namespace TakeoutSaaS.Application.Identity.Handlers;

/// <summary>
/// 角色模板复制处理器。
/// </summary>
public sealed class CopyRoleTemplateCommandHandler(
    IRoleTemplateRepository roleTemplateRepository,
    IRoleRepository roleRepository,
    IPermissionRepository permissionRepository,
    IRolePermissionRepository rolePermissionRepository)
    : IRequestHandler<CopyRoleTemplateCommand, RoleDto>
{
    /// <inheritdoc />
    public async Task<RoleDto> Handle(CopyRoleTemplateCommand request, CancellationToken cancellationToken)
    {
        // 1. 校验 Portal 与 TenantId 参数
        if (request.Portal == PortalType.Admin && request.TenantId is not null)
        {
            throw new BusinessException(ErrorCodes.ValidationFailed, "Portal=Admin 时 TenantId 必须为空");
        }

        if (request.Portal == PortalType.Tenant && (!request.TenantId.HasValue || request.TenantId.Value <= 0))
        {
            throw new BusinessException(ErrorCodes.ValidationFailed, "Portal=Tenant 时 TenantId 必须大于 0");
        }

        // 2. 查询模板与模板权限
        var template = await roleTemplateRepository.FindByCodeAsync(request.TemplateCode, cancellationToken)
            ?? throw new BusinessException(ErrorCodes.NotFound, $"角色模板 {request.TemplateCode} 不存在");

        var templatePermissions = await roleTemplateRepository.GetPermissionsAsync(template.Id, cancellationToken);
        var permissionCodes = templatePermissions
            .Select(x => x.PermissionCode)
            .Where(code => !string.IsNullOrWhiteSpace(code))
            .Distinct(StringComparer.OrdinalIgnoreCase)
            .ToArray();

        // 3. 计算角色名称/编码与描述
        var portal = request.Portal;
        var tenantId = request.TenantId;
        
        // 4. (空行后) 解析目标角色信息
        var roleCode = string.IsNullOrWhiteSpace(request.RoleCode) ? template.TemplateCode : request.RoleCode.Trim();
        var roleName = string.IsNullOrWhiteSpace(request.RoleName) ? template.Name : request.RoleName.Trim();
        var roleDescription = request.Description ?? template.Description;

        // 5. 准备或更新角色主体（幂等创建）。
        var role = await roleRepository.FindByCodeAsync(portal, tenantId, roleCode, cancellationToken);
        if (role is null)
        {
            role = new Role
            {
                Portal = portal,
                TenantId = tenantId,
                Name = roleName,
                Code = roleCode,
                Description = roleDescription
            };
            await roleRepository.AddAsync(role, cancellationToken);
        }
        else
        {
            if (!string.IsNullOrWhiteSpace(request.RoleName))
            {
                role.Name = roleName;
            }

            if (request.Description is not null)
            {
                role.Description = roleDescription;
            }

            await roleRepository.UpdateAsync(role, cancellationToken);
        }

        // 6. 确保模板权限全部存在，不存在则按模板定义创建。
        var existingPermissions = await permissionRepository.GetByCodesAsync(permissionCodes, cancellationToken);
        var permissionMap = existingPermissions.ToDictionary(x => x.Code, StringComparer.OrdinalIgnoreCase);

        foreach (var code in permissionCodes)
        {
            if (permissionMap.ContainsKey(code))
            {
                continue;
            }

            var permission = new Permission
            {
                Name = code,
                Code = code,
                Description = code
            };

            await permissionRepository.AddAsync(permission, cancellationToken);
            permissionMap[code] = permission;
        }

        await roleRepository.SaveChangesAsync(cancellationToken);

        // 7. 绑定缺失的权限，保留租户自定义的已有授权。
        var rolePermissions = await rolePermissionRepository.GetByRoleIdsAsync(portal, tenantId, new[] { role.Id }, cancellationToken);
        var existingPermissionIds = rolePermissions
            .Select(x => x.PermissionId)
            .ToHashSet();

        var targetPermissionIds = permissionCodes
            .Select(code => permissionMap[code].Id)
            .ToHashSet();

        var toAdd = targetPermissionIds.Except(existingPermissionIds).ToArray();
        if (toAdd.Length > 0)
        {
            var relations = toAdd.Select(permissionId => new RolePermission
            {
                Portal = portal,
                TenantId = tenantId,
                RoleId = role.Id,
                PermissionId = permissionId
            });

            await rolePermissionRepository.AddRangeAsync(relations, cancellationToken);
        }

        await rolePermissionRepository.SaveChangesAsync(cancellationToken);

        return new RoleDto
        {
            Id = role.Id,
            Portal = role.Portal,
            TenantId = role.TenantId,
            Name = role.Name,
            Code = role.Code,
            Description = role.Description
        };
    }
}
