using Fengling.Console.Models.Dtos;
using Fengling.Platform.Domain.AggregatesModel.UserAggregate;
using Fengling.Platform.Domain.AggregatesModel.RoleAggregate;
using Fengling.Platform.Infrastructure;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using System.Security.Claims;

namespace Fengling.Console.Services;

public interface IRoleService
{
    Task<(IEnumerable<RoleDto> Items, int TotalCount)> GetRolesAsync(int page, int pageSize, string? name = null,
        string? tenantId = null);

    Task<RoleDto?> GetRoleAsync(long id);
    Task<IEnumerable<UserDto>> GetRoleUsersAsync(long id);
    Task<RoleDto> CreateRoleAsync(CreateRoleDto dto);
    Task<RoleDto> UpdateRoleAsync(long id, UpdateRoleDto dto);
    Task DeleteRoleAsync(long id);
    Task AddUserToRoleAsync(long roleId, long userId);
    Task RemoveUserFromRoleAsync(long roleId, long userId);
}

public class RoleService : IRoleService
{
    private readonly ITenantManager _tenantManager;
    private readonly UserManager<ApplicationUser> _userManager;
    private readonly RoleManager<ApplicationRole> _roleManager;
    private readonly PlatformDbContext _context;
    private readonly IHttpContextAccessor _httpContextAccessor;

    public RoleService(
        ITenantManager tenantManager,
        UserManager<ApplicationUser> userManager,
        RoleManager<ApplicationRole> roleManager,
        PlatformDbContext context,
        IHttpContextAccessor httpContextAccessor)
    {
        _tenantManager = tenantManager;
        _userManager = userManager;
        _roleManager = roleManager;
        _context = context;
        _httpContextAccessor = httpContextAccessor;
    }

    public async Task<(IEnumerable<RoleDto> Items, int TotalCount)> GetRolesAsync(int page, int pageSize,
        string? name = null, string? tenantId = null)
    {
        var query = _context.Roles.AsQueryable();

        if (!string.IsNullOrEmpty(name))
        {
            query = query.Where(r => r.Name != null && r.Name.Contains(name));
        }

        if (!string.IsNullOrEmpty(tenantId))
        {
            query = query.Where(r => r.TenantId.HasValue && r.TenantId.Value.ToString().Contains(tenantId));
        }

        var totalCount = await query.CountAsync();
        var roles = await query
            .OrderByDescending(r => r.CreatedTime)
            .Skip((page - 1) * pageSize)
            .Take(pageSize)
            .ToListAsync();

        var roleDtos = new List<RoleDto>();
        foreach (var role in roles)
        {
            var users = await _userManager.GetUsersInRoleAsync(role.Name!);
            roleDtos.Add(new RoleDto
            {
                Id = role.Id,
                Name = role.Name,
                DisplayName = role.DisplayName,
                Description = role.Description,
                TenantId = role.TenantId,
                IsSystem = role.IsSystem,
                Permissions = role.Permissions,
                UserCount = users.Count,
                CreatedAt = role.CreatedTime
            });
        }

        return (roleDtos, totalCount);
    }

    public async Task<RoleDto?> GetRoleAsync(long id)
    {
        var role = await _context.Roles.FindAsync(id);
        if (role == null) return null;

        return new RoleDto
        {
            Id = role.Id,
            Name = role.Name,
            DisplayName = role.DisplayName,
            Description = role.Description,
            TenantId = role.TenantId,
            IsSystem = role.IsSystem,
            Permissions = role.Permissions,
            UserCount = (await _userManager.GetUsersInRoleAsync(role.Name!)).Count,
            CreatedAt = role.CreatedTime
        };
    }

    public async Task<IEnumerable<UserDto>> GetRoleUsersAsync(long id)
    {
        var role = await _context.Roles.FindAsync(id);
        if (role == null)
        {
            throw new KeyNotFoundException($"Role with ID {id} not found");
        }

        var users = await _userManager.GetUsersInRoleAsync(role.Name!);
        var userDtos = new List<UserDto>();

        foreach (var user in users)
        {
            var roles = await _userManager.GetRolesAsync(user);
            var tenant = user.TenantInfo.TenantId > 0 ? await _tenantManager.FindByIdAsync(user.TenantInfo.TenantId) : null;
            userDtos.Add(new UserDto
            {
                Id = user.Id,
                UserName = user.UserName,
                Email = user.Email,
                RealName = user.RealName,
                TenantCode = user.TenantInfo.TenantId.ToString(),
                TenantName = tenant?.Name ?? "",
                Roles = roles.ToList(),
                EmailConfirmed = user.EmailConfirmed,
                IsActive = !user.LockoutEnabled || user.LockoutEnd == null || user.LockoutEnd < DateTimeOffset.UtcNow,
                CreatedAt = user.CreatedTime
            });
        }

        return userDtos;
    }

    public async Task<RoleDto> CreateRoleAsync(CreateRoleDto dto)
    {
        var role = new ApplicationRole
        {
            Name = dto.Name,
            DisplayName = dto.DisplayName,
            Description = dto.Description,
            TenantId = dto.TenantId.HasValue ? dto.TenantId.Value : null,
            Permissions = dto.Permissions,
            IsSystem = false,
            CreatedTime = DateTime.UtcNow
        };

        var result = await _roleManager.CreateAsync(role);
        if (!result.Succeeded)
        {
            throw new InvalidOperationException(string.Join(", ", result.Errors.Select(e => e.Description)));
        }

        await CreateAuditLog("role", "create", "Role", role.Id, role.DisplayName, null,
            System.Text.Json.JsonSerializer.Serialize(dto));

        return new RoleDto
        {
            Id = role.Id,
            Name = role.Name,
            DisplayName = role.DisplayName,
            Description = role.Description,
            TenantId = role.TenantId,
            IsSystem = role.IsSystem,
            Permissions = role.Permissions,
            UserCount = 0,
            CreatedAt = role.CreatedTime
        };
    }

    public async Task<RoleDto> UpdateRoleAsync(long id, UpdateRoleDto dto)
    {
        var role = await _context.Roles.FindAsync(id);
        if (role == null)
        {
            throw new KeyNotFoundException($"Role with ID {id} not found");
        }

        if (role.IsSystem)
        {
            throw new InvalidOperationException("系统角色不能修改");
        }

        var oldValue = System.Text.Json.JsonSerializer.Serialize(role);

        role.DisplayName = dto.DisplayName;
        role.Description = dto.Description;
        role.Permissions = dto.Permissions;

        await _roleManager.UpdateAsync(role);

        await CreateAuditLog("role", "update", "Role", role.Id, role.DisplayName, oldValue,
            System.Text.Json.JsonSerializer.Serialize(role));

        var users = await _userManager.GetUsersInRoleAsync(role.Name!);
        return new RoleDto
        {
            Id = role.Id,
            Name = role.Name,
            DisplayName = role.DisplayName,
            Description = role.Description,
            TenantId = role.TenantId,
            IsSystem = role.IsSystem,
            Permissions = role.Permissions,
            UserCount = users.Count,
            CreatedAt = role.CreatedTime
        };
    }

    public async Task DeleteRoleAsync(long id)
    {
        var role = await _context.Roles.FindAsync(id);
        if (role == null)
        {
            throw new KeyNotFoundException($"Role with ID {id} not found");
        }

        if (role.IsSystem)
        {
            throw new InvalidOperationException("系统角色不能删除");
        }

        var oldValue = System.Text.Json.JsonSerializer.Serialize(role);
        var users = await _userManager.GetUsersInRoleAsync(role.Name!);

        foreach (var user in users)
        {
            await _userManager.RemoveFromRoleAsync(user, role.Name!);
        }

        await _roleManager.DeleteAsync(role);

        await CreateAuditLog("role", "delete", "Role", role.Id, role.DisplayName, oldValue);
    }

    public async Task AddUserToRoleAsync(long roleId, long userId)
    {
        var role = await _context.Roles.FindAsync(roleId);
        if (role == null)
        {
            throw new KeyNotFoundException($"Role with ID {roleId} not found");
        }

        var user = await _userManager.FindByIdAsync(userId.ToString());
        if (user == null)
        {
            throw new KeyNotFoundException($"User with ID {userId} not found");
        }

        var result = await _userManager.AddToRoleAsync(user, role.Name!);
        if (!result.Succeeded)
        {
            throw new InvalidOperationException(string.Join(", ", result.Errors.Select(e => e.Description)));
        }

        await CreateAuditLog("role", "update", "UserRole", null, $"{role.Name} - {user.UserName}");
    }

    public async Task RemoveUserFromRoleAsync(long roleId, long userId)
    {
        var role = await _context.Roles.FindAsync(roleId);
        if (role == null)
        {
            throw new KeyNotFoundException($"Role with ID {roleId} not found");
        }

        var user = await _userManager.FindByIdAsync(userId.ToString());
        if (user == null)
        {
            throw new KeyNotFoundException($"User with ID {userId} not found");
        }

        var result = await _userManager.RemoveFromRoleAsync(user, role.Name!);
        if (!result.Succeeded)
        {
            throw new InvalidOperationException(string.Join(", ", result.Errors.Select(e => e.Description)));
        }

        await CreateAuditLog("role", "update", "UserRole", null, $"{role.Name} - {user.UserName}");
    }

    private async Task CreateAuditLog(string operation, string action, string targetType, long? targetId,
        string? targetName, string? oldValue = null, string? newValue = null)
    {
        var httpContext = _httpContextAccessor.HttpContext;
        var userName = httpContext?.User?.FindFirstValue(ClaimTypes.NameIdentifier) ??
                       httpContext?.User?.Identity?.Name ?? "system";
        var tenantId = httpContext?.User?.FindFirstValue("TenantId");

        var log = new AuditLog
        {
            Operator = userName,
            TenantId = tenantId,
            Operation = operation,
            Action = action,
            TargetType = targetType,
            TargetId = targetId,
            TargetName = targetName,
            IpAddress = httpContext?.Connection?.RemoteIpAddress?.ToString() ?? "unknown",
            Status = "success",
            OldValue = oldValue,
            NewValue = newValue,
            CreatedAt = DateTime.UtcNow
        };

        _context.AuditLogs.Add(log);
        await _context.SaveChangesAsync();
    }
}
