using FastEndpoints;
using QuantEngine.Core.Interfaces;
using QuantEngine.Core.Models;
using System.Security.Cryptography;
using System.Text;
using FluentValidation;

namespace QuantEngine.Web.Endpoints;

// DTO Models with Data Annotations
public class UserDto
{
    public string Username { get; set; } = string.Empty;
    public string Role { get; set; } = "Viewer";
    public bool IsActive { get; set; } = true;
    public string CreatedAt { get; set; } = string.Empty;
    public string UpdatedAt { get; set; } = string.Empty;
}

public class CreateUserRequest
{
    public string Username { get; set; } = string.Empty;
    public string Password { get; set; } = string.Empty;
    public string Role { get; set; } = "Viewer";
}

public class UpdateUserRequest
{
    public string Username { get; set; } = string.Empty;
    public string? Password { get; set; } // Optional password change
    public string Role { get; set; } = "Viewer";
    public bool IsActive { get; set; } = true;
}

public class DeleteUserRequest
{
    public string Username { get; set; } = string.Empty;
}

// FluentValidation rules for CreateUserRequest
public class CreateUserValidator : Validator<CreateUserRequest>
{
    public CreateUserValidator()
    {
        RuleFor(x => x.Username)
            .NotEmpty().WithMessage("사용자 ID는 필수 입력값입니다.")
            .MinimumLength(3).WithMessage("사용자 ID는 최소 3자 이상이어야 합니다.");
        
        RuleFor(x => x.Password)
            .NotEmpty().WithMessage("비밀번호는 필수 입력값입니다.")
            .MinimumLength(4).WithMessage("비밀번호는 최소 4자 이상이어야 합니다.");
            
        RuleFor(x => x.Role)
            .Must(role => role == "Admin" || role == "Operator" || role == "Viewer")
            .WithMessage("올바르지 않은 역할 권한입니다.");
    }
}

// FluentValidation rules for UpdateUserRequest
public class UpdateUserValidator : Validator<UpdateUserRequest>
{
    public UpdateUserValidator()
    {
        RuleFor(x => x.Username)
            .NotEmpty().WithMessage("사용자 ID는 필수 입력값입니다.");

        RuleFor(x => x.Password)
            .MinimumLength(4).When(x => !string.IsNullOrEmpty(x.Password))
            .WithMessage("새 비밀번호는 최소 4자 이상이어야 합니다.");

        RuleFor(x => x.Role)
            .Must(role => role == "Admin" || role == "Operator" || role == "Viewer")
            .WithMessage("올바르지 않은 역할 권한입니다.");
    }
}

// 1. GET ALL USERS
public class GetUsersEndpoint : EndpointWithoutRequest<List<UserDto>>
{
    private readonly IWorkspaceRepository _repo;

    public GetUsersEndpoint(IWorkspaceRepository repo)
    {
        _repo = repo;
    }

    public override void Configure()
    {
        Get("/api/users");
        // Secure access in prod, allow for current logged users
        AllowAnonymous();
        Description(d => d
            .Produces<List<UserDto>>(200)
            .Produces(500));
    }

    public override async Task HandleAsync(CancellationToken ct)
    {
        try
        {
            var accounts = await _repo.GetAccountsAsync();
            var dtos = accounts.Select(a => new UserDto
            {
                Username = a.Username,
                Role = a.Role,
                IsActive = string.Equals(a.IsActive, "true", StringComparison.OrdinalIgnoreCase),
                CreatedAt = a.CreatedAt,
                UpdatedAt = a.UpdatedAt
            }).ToList();

            await SendOkAsync(dtos, ct);
        }
        catch
        {
            await SendErrorsAsync(500, ct);
        }
    }
}

// 2. CREATE USER
public class CreateUserEndpoint : Endpoint<CreateUserRequest, UserDto>
{
    private readonly IWorkspaceRepository _repo;

    public CreateUserEndpoint(IWorkspaceRepository repo)
    {
        _repo = repo;
    }

    public override void Configure()
    {
        Post("/api/users");
        AllowAnonymous();
        Description(d => d
            .Produces<UserDto>(200)
            .Produces(400)
            .Produces(500));
    }

    public override async Task HandleAsync(CreateUserRequest req, CancellationToken ct)
    {
        try
        {
            var existing = await _repo.GetAccountByUsernameAsync(req.Username.Trim());
            if (existing != null)
            {
                AddError("이미 존재하는 사용자 ID입니다.");
                await SendErrorsAsync(400, ct);
                return;
            }

            var passwordHash = Convert.ToHexString(SHA256.HashData(Encoding.UTF8.GetBytes(req.Password)));
            var now = DateTimeOffset.UtcNow.ToString("O");

            var newAccount = new WorkspaceAccount
            {
                Username = req.Username.Trim(),
                PasswordHash = passwordHash,
                Role = req.Role,
                IsActive = "true",
                CreatedAt = now,
                UpdatedAt = now
            };

            var success = await _repo.UpsertAccountAsync(newAccount);
            if (!success)
            {
                await SendErrorsAsync(500, ct);
                return;
            }

            await SendOkAsync(new UserDto
            {
                Username = newAccount.Username,
                Role = newAccount.Role,
                IsActive = true,
                CreatedAt = newAccount.CreatedAt,
                UpdatedAt = newAccount.UpdatedAt
            }, ct);
        }
        catch
        {
            await SendErrorsAsync(500, ct);
        }
    }
}

// 3. UPDATE USER
public class UpdateUserEndpoint : Endpoint<UpdateUserRequest, UserDto>
{
    private readonly IWorkspaceRepository _repo;

    public UpdateUserEndpoint(IWorkspaceRepository repo)
    {
        _repo = repo;
    }

    public override void Configure()
    {
        Put("/api/users");
        AllowAnonymous();
        Description(d => d
            .Produces<UserDto>(200)
            .Produces(404)
            .Produces(500));
    }

    public override async Task HandleAsync(UpdateUserRequest req, CancellationToken ct)
    {
        try
        {
            var existing = await _repo.GetAccountByUsernameAsync(req.Username.Trim());
            if (existing == null)
            {
                await SendNotFoundAsync(ct);
                return;
            }

            existing.Role = req.Role;
            existing.IsActive = req.IsActive ? "true" : "false";
            existing.UpdatedAt = DateTimeOffset.UtcNow.ToString("O");

            if (!string.IsNullOrWhiteSpace(req.Password))
            {
                existing.PasswordHash = Convert.ToHexString(SHA256.HashData(Encoding.UTF8.GetBytes(req.Password)));
            }

            var success = await _repo.UpsertAccountAsync(existing);
            if (!success)
            {
                await SendErrorsAsync(500, ct);
                return;
            }

            await SendOkAsync(new UserDto
            {
                Username = existing.Username,
                Role = existing.Role,
                IsActive = req.IsActive,
                CreatedAt = existing.CreatedAt,
                UpdatedAt = existing.UpdatedAt
            }, ct);
        }
        catch
        {
            await SendErrorsAsync(500, ct);
        }
    }
}

// 4. DELETE USER
public class DeleteUserEndpoint : Endpoint<DeleteUserRequest>
{
    private readonly IWorkspaceRepository _repo;

    public DeleteUserEndpoint(IWorkspaceRepository repo)
    {
        _repo = repo;
    }

    public override void Configure()
    {
        Delete("/api/users");
        AllowAnonymous();
        Description(d => d
            .Produces(200)
            .Produces(404)
            .Produces(500));
    }

    public override async Task HandleAsync(DeleteUserRequest req, CancellationToken ct)
    {
        try
        {
            var existing = await _repo.GetAccountByUsernameAsync(req.Username.Trim());
            if (existing == null)
            {
                await SendNotFoundAsync(ct);
                return;
            }

            // Deactivate instead of physical delete to preserve audit history and avoid triggers
            existing.IsActive = "false";
            existing.UpdatedAt = DateTimeOffset.UtcNow.ToString("O");

            var success = await _repo.UpsertAccountAsync(existing);
            if (!success)
            {
                await SendErrorsAsync(500, ct);
                return;
            }

            await SendOkAsync(ct);
        }
        catch
        {
            await SendErrorsAsync(500, ct);
        }
    }
}
