using Fengling.AuthService.Data;
using Fengling.AuthService.Models;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using System.Security.Claims;

namespace Fengling.AuthService.Controllers;

[ApiController]
[Route("api/[controller]")]
[Authorize]
public class RolesController : ControllerBase
{
    private readonly ApplicationDbContext _context;
    private readonly RoleManager<ApplicationRole> _roleManager;
    private readonly UserManager<ApplicationUser> _userManager;
    private readonly ILogger<RolesController> _logger;

    public RolesController(
        ApplicationDbContext context,
        RoleManager<ApplicationRole> roleManager,
        UserManager<ApplicationUser> userManager,
        ILogger<RolesController> logger)
    {
        _context = context;
        _roleManager = roleManager;
        _userManager = userManager;
        _logger = logger;
    }

    [HttpGet]
    public async Task<ActionResult<object>> GetRoles(
        [FromQuery] int page = 1,
        [FromQuery] int pageSize = 10,
        [FromQuery] string? name = null,
        [FromQuery] 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.ToString() == tenantId);
        }

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

        var result = new List<object>();

        foreach (var role in roles)
        {
            var users = await _userManager.GetUsersInRoleAsync(role.Name!);
            result.Add(new
            {
                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 Ok(new
        {
            items = result,
            totalCount,
            page,
            pageSize
        });
    }

    [HttpGet("{id}")]
    public async Task<ActionResult<ApplicationRole>> GetRole(long id)
    {
        var role = await _context.Roles.FindAsync(id);
        if (role == null)
        {
            return NotFound();
        }

        return Ok(new
        {
            id = role.Id,
            name = role.Name,
            displayName = role.DisplayName,
            description = role.Description,
            tenantId = role.TenantId,
            isSystem = role.IsSystem,
            permissions = role.Permissions,
            createdAt = role.CreatedTime,
        });
    }

    [HttpGet("{id}/users")]
    public async Task<ActionResult<List<object>>> GetRoleUsers(long id)
    {
        var role = await _context.Roles.FindAsync(id);
        if (role == null)
        {
            return NotFound();
        }

        var users = await _userManager.GetUsersInRoleAsync(role.Name!);

        var result = users.Select(async u => new
        {
            id = u.Id,
            userName = u.UserName,
            email = u.Email,
            realName = u.RealName,
            tenantId = u.TenantInfo.TenantId,
            roles = await _userManager.GetRolesAsync(u),
            isActive = !u.LockoutEnabled || u.LockoutEnd == null || u.LockoutEnd < DateTimeOffset.UtcNow,
            createdAt = u.CreatedTime,
        });

        return Ok(await Task.WhenAll(result));
    }

    [HttpPost]
    public async Task<ActionResult<ApplicationRole>> CreateRole(CreateRoleDto dto)
    {
        var role = new ApplicationRole
        {
            Name = dto.Name,
            DisplayName = dto.DisplayName,
            Description = dto.Description,
            TenantId = dto.TenantId,
            Permissions = dto.Permissions,
            IsSystem = false,
            CreatedTime = DateTime.UtcNow,
        };

        var result = await _roleManager.CreateAsync(role);
        if (!result.Succeeded)
        {
            return BadRequest(result.Errors);
        }

        await CreateAuditLog("role", "create", "Role", role.Id, role.DisplayName, null, SerializeToJson(dto));

        return CreatedAtAction(nameof(GetRole), new { id = role.Id }, role);
    }

    [HttpPut("{id}")]
    public async Task<IActionResult> UpdateRole(long id, UpdateRoleDto dto)
    {
        var role = await _context.Roles.FindAsync(id);
        if (role == null)
        {
            return NotFound();
        }

        if (role.IsSystem)
        {
            return BadRequest("系统角色不能修改");
        }

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

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

        await _context.SaveChangesAsync();

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

        return NoContent();
    }

    [HttpDelete("{id}")]
    public async Task<IActionResult> DeleteRole(long id)
    {
        var role = await _context.Roles.FindAsync(id);
        if (role == null)
        {
            return NotFound();
        }

        if (role.IsSystem)
        {
            return BadRequest("系统角色不能删除");
        }

        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!);
        }

        _context.Roles.Remove(role);
        await _context.SaveChangesAsync();

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

        return NoContent();
    }

    [HttpDelete("{id}/users/{userId}")]
    public async Task<IActionResult> RemoveUserFromRole(long id, long userId)
    {
        var role = await _context.Roles.FindAsync(id);
        if (role == null)
        {
            return NotFound();
        }

        var user = await _userManager.FindByIdAsync(userId.ToString());
        if (user == null)
        {
            return NotFound();
        }

        var result = await _userManager.RemoveFromRoleAsync(user, role.Name!);
        if (!result.Succeeded)
        {
            return BadRequest(result.Errors);
        }

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

        return NoContent();
    }

    private async Task CreateAuditLog(string operation, string action, string targetType, long? targetId, string? targetName, string? oldValue = null, string? newValue = null)
    {
        var userName = User.FindFirstValue(ClaimTypes.NameIdentifier) ?? User.Identity?.Name ?? "system";
        var tenantId = 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,
        };

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

    private string SerializeToJson(object obj)
    {
        return System.Text.Json.JsonSerializer.Serialize(obj, new System.Text.Json.JsonSerializerOptions
        {
            WriteIndented = false
        });
    }
}

public class CreateRoleDto
{
    public string Name { get; set; } = string.Empty;
    public string DisplayName { get; set; } = string.Empty;
    public string? Description { get; set; }
    public long? TenantId { get; set; }
    public List<string> Permissions { get; set; } = new();
}

public class UpdateRoleDto
{
    public string DisplayName { get; set; } = string.Empty;
    public string? Description { get; set; }
    public List<string> Permissions { get; set; } = new();
}
