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;
using Fengling.Platform.Domain.AggregatesModel.TenantAggregate;
using Fengling.Platform.Infrastructure;

namespace Fengling.AuthService.Controllers;

[ApiController]
[Route("api/[controller]")]
[Authorize]
public class UsersController(
    ApplicationDbContext context,
    UserManager<ApplicationUser> userManager,
    RoleManager<ApplicationRole> roleManager,
    ILogger<UsersController> logger,
    PlatformDbContext platformDbContext)
    : ControllerBase
{

    [HttpGet]
    public async Task<ActionResult<object>> GetUsers(
        [FromQuery] int page = 1,
        [FromQuery] int pageSize = 10,
        [FromQuery] string? userName = null,
        [FromQuery] string? email = null,
        [FromQuery] string? tenantCode = null)
    {
        var query = context.Users.AsQueryable();

        if (!string.IsNullOrEmpty(userName))
        {
            query = query.Where(u => u.UserName!.Contains(userName));
        }

        if (!string.IsNullOrEmpty(email))
        {
            query = query.Where(u => u.Email != null && u.Email.Contains(email));
        }

        if (!string.IsNullOrEmpty(tenantCode))
        {
            query = query.Where(u => u.TenantInfo.TenantCode == tenantCode);
        }

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

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

        return Ok(new
        {
            items = await Task.WhenAll(result),
            totalCount,
            page,
            pageSize
        });
    }

    [HttpGet("{id}")]
    public async Task<ActionResult<object>> GetUser(long id)
    {
        var user = await context.Users.FindAsync(id);
        if (user == null)
        {
            return NotFound();
        }

        var roles = await userManager.GetRolesAsync(user);

        return Ok(new
        {
            id = user.Id,
            userName = user.UserName,
            email = user.Email,
            realName = user.RealName,
            phone = user.Phone,
            tenantId = user.TenantInfo.TenantId,
            roles,
            emailConfirmed = user.EmailConfirmed,
            isActive = !user.LockoutEnabled || user.LockoutEnd == null || user.LockoutEnd < DateTimeOffset.UtcNow,
            createdAt = user.CreatedTime,
        });
    }

    [HttpPost]
    public async Task<ActionResult<ApplicationUser>> CreateUser(CreateUserDto dto)
    {
        var tenantId = dto.TenantId ?? 0;
        Tenant? tenant = null;

        if (tenantId != 0)
        {
            tenant = await platformDbContext.Tenants.FindAsync(tenantId);
            if (tenant == null)
            {
                return BadRequest("Invalid tenant ID");
            }
        }

        var user = new ApplicationUser
        {
            UserName = dto.UserName,
            Email = dto.Email,
            RealName = dto.RealName,
            Phone = dto.Phone,
            TenantInfo = new TenantInfo(tenant!),
            EmailConfirmed = dto.EmailConfirmed,
            CreatedTime = DateTime.UtcNow,
        };

        var result = await userManager.CreateAsync(user, dto.Password);
        if (!result.Succeeded)
        {
            return BadRequest(result.Errors);
        }

        if (dto.RoleIds.Any())
        {
            foreach (var roleId in dto.RoleIds)
            {
                var role = await roleManager.FindByIdAsync(roleId.ToString());
                if (role != null)
                {
                    await userManager.AddToRoleAsync(user, role.Name!);
                }
            }
        }

        if (!dto.IsActive)
        {
            await userManager.SetLockoutEnabledAsync(user, true);
            await userManager.SetLockoutEndDateAsync(user, DateTimeOffset.MaxValue);
        }

        await CreateAuditLog("user", "create", "User", user.Id, user.UserName, null, SerializeToJson(dto));

        return CreatedAtAction(nameof(GetUser), new { id = user.Id }, user);
    }

    [HttpPut("{id}")]
    public async Task<IActionResult> UpdateUser(long id, UpdateUserDto dto)
    {
        var user = await context.Users.FindAsync(id);
        if (user == null)
        {
            return NotFound();
        }

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

        user.Email = dto.Email;
        user.RealName = dto.RealName;
        user.Phone = dto.Phone;
        user.EmailConfirmed = dto.EmailConfirmed;
        user.UpdatedTime = DateTime.UtcNow;

        if (dto.IsActive)
        {
            await userManager.SetLockoutEnabledAsync(user, false);
            await userManager.SetLockoutEndDateAsync(user, null);
        }
        else
        {
            await userManager.SetLockoutEnabledAsync(user, true);
            await userManager.SetLockoutEndDateAsync(user, DateTimeOffset.MaxValue);
        }

        await context.SaveChangesAsync();

        await CreateAuditLog("user", "update", "User", user.Id, user.UserName, oldValue, System.Text.Json.JsonSerializer.Serialize(user));

        return NoContent();
    }

    [HttpPut("{id}/password")]
    public async Task<IActionResult> ResetPassword(long id, ResetPasswordDto dto)
    {
        var user = await userManager.FindByIdAsync(id.ToString());
        if (user == null)
        {
            return NotFound();
        }

        var token = await userManager.GeneratePasswordResetTokenAsync(user);
        var result = await userManager.ResetPasswordAsync(user, token, dto.NewPassword);

        if (!result.Succeeded)
        {
            return BadRequest(result.Errors);
        }

        await CreateAuditLog("user", "reset_password", "User", user.Id, user.UserName);

        return NoContent();
    }

    [HttpDelete("{id}")]
    public async Task<IActionResult> DeleteUser(long id)
    {
        var user = await context.Users.FindAsync(id);
        if (user == null)
        {
            return NotFound();
        }

        var oldValue = System.Text.Json.JsonSerializer.Serialize(user);
        user.IsDeleted = true;
        user.UpdatedTime = DateTime.UtcNow;
        await context.SaveChangesAsync();

        await CreateAuditLog("user", "delete", "User", user.Id, user.UserName, oldValue);

        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 CreateUserDto
{
    public string UserName { get; set; } = string.Empty;
    public string Email { get; set; } = string.Empty;
    public string RealName { get; set; } = string.Empty;
    public string? Phone { get; set; }
    public long? TenantId { get; set; }
    public List<long> RoleIds { get; set; } = new();
    public string Password { get; set; } = string.Empty;
    public bool EmailConfirmed { get; set; }
    public bool IsActive { get; set; } = true;
}

public class UpdateUserDto
{
    public string Email { get; set; } = string.Empty;
    public string RealName { get; set; } = string.Empty;
    public string? Phone { get; set; }
    public bool EmailConfirmed { get; set; }
    public bool IsActive { get; set; } = true;
}

public class ResetPasswordDto
{
    public string NewPassword { get; set; } = string.Empty;
}
