using SharedKernel;
using SharedKernel.Results;
using Application.Abstractions.Authentication;
using Application.Abstractions.Data;
using Application.Abstractions.Messaging;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Options;

namespace Application.Features.Api.Auth.RefreshToken;

internal sealed class Handler(
    IAppDbContext db,
    IJwtTokenProvider jwtTokenProvider,
    IOptions<AppSettings> options
) : ICommandHandler<Command, Result<Response>> {

    private readonly AppSettings.JwtSection _jwt = options.Value.JWT;

    public async Task<Result<Response>> Handle(Command request, CancellationToken ct)
    {
        if (string.IsNullOrWhiteSpace(request.RefreshToken))
        {
            return Result.Failure<Response>(Error.Problem("Auth.TokenRequired", "refreshToken은 필수입니다."));
        }

        // 기존 RefreshToken 조회
        var existingToken = await db.RefreshToken.Include(x => x.Member).FirstOrDefaultAsync(x => x.Token == request.RefreshToken, ct);

        if (existingToken is null)
        {
            return Result.Failure<Response>(Error.NotFound("Auth.TokenNotFound", "유효하지 않은 refreshToken 입니다."));
        }

        if (!existingToken.IsActive)
        {
            return Result.Failure<Response>(Error.Problem("Auth.TokenExpired", "만료되었거나 취소된 refreshToken 입니다."));
        }

        if (existingToken.Member is null)
        {
            return Result.Failure<Response>(Error.NotFound("Auth.MemberNotFound", "회원 정보를 찾을 수 없습니다."));
        }

        var member = existingToken.Member;

        // 기존 토큰 폐기
        existingToken.Revoke();

        // 새 토큰 생성
        var accessToken = jwtTokenProvider.CreateAccessToken(member.ID, member.Email, member.Name);
        var newRefreshToken = jwtTokenProvider.CreateRefreshToken();
        var expiresAt = DateTime.UtcNow.AddMinutes(_jwt.AccessTokenExpiration);

        // 새 RefreshToken 저장
        var refreshTokenEntity = Domain.Entities.Members.RefreshToken.Create(
            member.ID,
            newRefreshToken,
            DateTime.UtcNow.AddDays(_jwt.RefreshTokenExpiration)
        );

        await db.RefreshToken.AddAsync(refreshTokenEntity, ct);
        await db.SaveChangesAsync(ct);

        return Result.Success(new Response(
            accessToken, newRefreshToken, expiresAt
        ));
    }
}