using Application.Abstractions.Data;
using Application.Abstractions.Messaging;
using Domain.Entities.EmailVerification.ValueObject;
using SharedKernel.Results;
using Microsoft.EntityFrameworkCore;

namespace Application.Features.Api.MyPage.VerifyEmail;

internal sealed class Handler(IAppDbContext db) : IQueryHandler<Query, Result>
{
    public async Task<Result> Handle(Query request, CancellationToken ct)
    {
        if (string.IsNullOrWhiteSpace(request.Token))
        {
            return Result.Failure(Error.Problem("MyPage.TokenRequired", "인증 토큰은 필수입니다."));
        }

        var verifyToken = await db.EmailVerifyToken.FirstOrDefaultAsync(t => t.Token == request.Token && t.Type == VerificationType.ChangedEmail, ct);

        if (verifyToken is null)
        {
            return Result.Failure(Error.NotFound("MyPage.TokenNotFound", "유효하지 않은 인증 토큰입니다."));
        }

        if (verifyToken.IsVerified)
        {
            return Result.Failure(Error.Problem("MyPage.AlreadyVerified", "이미 인증된 토큰입니다."));
        }

        if (verifyToken.Expiration < DateTime.UtcNow)
        {
            return Result.Failure(Error.Problem("MyPage.TokenExpired", "인증 토큰이 만료되었습니다."));
        }

        // AdditionalData.Email = 기존 이메일, verifyToken.Email = 새 이메일
        var oldEmail = verifyToken.AdditionalData?.Email;
        if (string.IsNullOrWhiteSpace(oldEmail))
        {
            return Result.Failure(Error.Problem("MyPage.InvalidToken", "인증 토큰 데이터가 올바르지 않습니다."));
        }

        var member = await db.Member.FirstOrDefaultAsync(m => m.Email == oldEmail, ct);
        if (member is null)
        {
            return Result.Failure(Error.NotFound("MyPage.MemberNotFound", "회원 정보를 찾을 수 없습니다."));
        }

        // 새 이메일이 다른 회원에게 사용 중인지 확인
        var newEmail = verifyToken.Email;
        var emailExists = await db.Member.AnyAsync(m => m.Email == newEmail && m.ID != member.ID, ct);
        if (emailExists)
        {
            return Result.Failure(Error.Conflict("MyPage.EmailExists", "이미 사용 중인 이메일입니다."));
        }

        verifyToken.MarkVerified();
        member.SetEmail(newEmail);
        member.MarkEmailVerified();

        await db.SaveChangesAsync(ct);

        return Result.Success();
    }
}