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

namespace Application.Features.Api.Auth.Register;

internal sealed class Handler(
    IAppDbContext db,
    ICacheService cache,
    IMailService mailService
) : ICommandHandler<Command, Result<RegisterResponse>> {

    public async Task<Result<RegisterResponse>> Handle(Command request, CancellationToken ct)
    {
        // 유효성 검사
        if (string.IsNullOrWhiteSpace(request.Email))
        {
            return Result.Failure<RegisterResponse>(Error.Problem("Auth.EmailRequired", "이메일은 필수입니다."));
        }

        if (string.IsNullOrWhiteSpace(request.Password))
        {
            return Result.Failure<RegisterResponse>(Error.Problem("Auth.PasswordRequired", "비밀번호는 필수입니다."));
        }

        // Config 로드
        var accountConfig = await AccountConfigLoader.GetAccountConfigAsync(cache, db, ct);

        // 회원가입 차단 확인
        if (accountConfig.IsRegisterBlock)
        {
            return Result.Failure<RegisterResponse>(Error.Problem("Auth.RegisterBlocked", "현재 회원가입이 차단되어 있습니다."));
        }

        // 비밀번호 복잡도 검증
        var passwordResult = PasswordPolicyValidator.Validate(request.Password, accountConfig);
        if (!passwordResult.IsSuccess)
        {
            return Result.Failure<RegisterResponse>(passwordResult.Error);
        }

        // 이메일 중복 체크
        var email = request.Email.Trim().ToLower();
        var exists = await db.Member.AnyAsync(m => m.Email == email, ct);

        if (exists)
        {
            return Result.Failure<RegisterResponse>(Error.Conflict("Auth.EmailExists", "이미 사용 중인 이메일입니다."));
        }

        // 금지 이메일 체크
        if (!string.IsNullOrWhiteSpace(accountConfig.DeniedEmailList))
        {
            var deniedEmails = accountConfig.DeniedEmailList
                .Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)
                .Select(e => e.ToLower());

            foreach (var denied in deniedEmails)
            {
                // 도메인 매칭 (@domain.com) 또는 전체 이메일 매칭
                if (denied.StartsWith('@') && email.EndsWith(denied))
                {
                    return Result.Failure<RegisterResponse>(Error.Problem("Auth.DeniedEmail", "사용할 수 없는 이메일입니다."));
                }
                else if (email == denied)
                {
                    return Result.Failure<RegisterResponse>(Error.Problem("Auth.DeniedEmail", "사용할 수 없는 이메일입니다."));
                }
            }
        }

        // 금지 별명 체크
        if (!string.IsNullOrWhiteSpace(request.Name) && !string.IsNullOrWhiteSpace(accountConfig.DeniedNameList))
        {
            var name = request.Name.Trim().ToLower();
            var deniedNames = accountConfig.DeniedNameList
                .Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)
                .Select(n => n.ToLower());

            if (deniedNames.Contains(name))
            {
                return Result.Failure<RegisterResponse>(Error.Problem("Auth.DeniedName", "사용할 수 없는 별명입니다."));
            }
        }

        // Member 생성 (비밀번호 해싱 포함)
        var member = Domain.Entities.Members.Member.Create(email, request.Password);

        await db.Member.AddAsync(member, ct);
        await db.SaveChangesAsync(ct);

        // Member ID 확보 후 연관 엔티티 생성
        await db.MemberApprove.AddAsync(
            Domain.Entities.Members.MemberApprove.Create(member.ID), ct
        );

        await db.MemberStats.AddAsync(
            Domain.Entities.Members.MemberStats.Create(member.ID), ct
        );

        await db.Wallet.AddAsync(
            Domain.Entities.Wallets.Wallet.Create(member.ID), ct
        );

        await db.SaveChangesAsync(ct);

        // 이메일 인증이 필요한 경우 인증번호 발송
        if (accountConfig.IsRegisterEmailAuth)
        {
            // 기존 미인증 코드 삭제
            var existing = await db.EmailVerifyNumber
                .Where(e => e.Email == email && e.Type == VerificationType.Registration && !e.IsVerified)
                .ToListAsync(ct);
            db.EmailVerifyNumber.RemoveRange(existing);

            // 6자리 랜덤 숫자 코드 생성
            var code = Random.Shared.Next(100000, 999999).ToString();
            var verifyNumber = EmailVerifyNumber.Create(
                VerificationType.Registration,
                email,
                code,
                DateTime.UtcNow.AddMinutes(5)
            );

            await db.EmailVerifyNumber.AddAsync(verifyNumber, ct);
            await db.SaveChangesAsync(ct);

            // 인증번호 이메일 발송
            await mailService.SendAsync(new SendData(
                email,
                "[bitforum] 회원가입 이메일 인증번호",
                $"<p>회원가입 이메일 인증번호입니다.</p><p><strong>{code}</strong></p><p>이 코드는 5분간 유효합니다.</p>"
            ), ct);
        }

        return Result.Success(new RegisterResponse(member.ID, accountConfig.IsRegisterEmailAuth));
    }
}
