using Application.Abstractions.Authentication;
using Application.Abstractions.Cache;
using Application.Abstractions.Data;
using Application.Helpers;
using Domain.Entities.Members.Logs;
using MediatR;
using SharedKernel;
using SharedKernel.Results;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;

namespace Application.Features.Api.Auth.GoogleLogin;

internal sealed class Handler(
    IAppDbContext db,
    IJwtTokenProvider jwtTokenProvider,
    IGoogleTokenValidator googleTokenValidator,
    IOptions<AppSettings> options,
    ILogger<Handler> logger,
    ICacheService cache
) : IRequestHandler<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.Credential))
        {
            return Result.Failure<Response>(Error.Problem("Auth.CredentialRequired", "Google 인증 정보가 필요합니다."));
        }

        // Config에서 GoogleClientId 조회
        var config = await db.Config.AsNoTracking().OrderByDescending(x => x.ID).FirstOrDefaultAsync(ct);
        var googleClientId = config?.External.GoogleClientId;

        if (string.IsNullOrWhiteSpace(googleClientId))
        {
            return Result.Failure<Response>(Error.Problem("Auth.GoogleNotConfigured", "Google 로그인이 설정되지 않았습니다."));
        }

        // Google ID Token 검증
        var googleUser = await googleTokenValidator.ValidateAsync(request.Credential, googleClientId, ct);

        if (googleUser is null)
        {
            return Result.Failure<Response>(Error.Unauthorized("Auth.GoogleTokenInvalid", "Google 인증에 실패했습니다."));
        }

        var email = googleUser.Email.Trim().ToLower();

        // Member 조회
        var member = await db.Member.FirstOrDefaultAsync(m => m.Email == email, ct);

        if (member is null)
        {
            // 자동 회원가입
            var accountConfig = await AccountConfigLoader.GetAccountConfigAsync(cache, db, ct);

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

            member = Domain.Entities.Members.Member.Create(email);

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

            // 연관 엔티티 생성
            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
            );

            // 구글 인증이므로 이메일 인증 완료 처리
            member.MarkEmailVerified();

            await db.SaveChangesAsync(ct);

            logger.LogInformation("Google 자동 회원가입: {Email}", email);
        }

        // 탈퇴 회원 거부
        if (member.IsWithdraw)
        {
            await LogLoginAttempt(member.ID, false, email, "탈퇴 회원 (Google)", request, ct);
            return Result.Failure<Response>(Error.Problem("Auth.MemberWithdrawn", "탈퇴한 회원은 이용할 수 없습니다."));
        }

        // 차단 회원 거부
        if (member.IsDenied)
        {
            await LogLoginAttempt(member.ID, false, email, "차단 회원 (Google)", request, ct);
            return Result.Failure<Response>(Error.Problem("Auth.MemberDenied", "차단된 회원이므로 이용할 수 없습니다."));
        }

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

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

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

        // 로그인 횟수 증가
        var memberStats = await db.MemberStats.FirstOrDefaultAsync(x => x.MemberID == member.ID, ct);
        if (memberStats is not null)
        {
            memberStats.LoginCount++;
        }

        // 성공 로그 기록
        await LogLoginAttempt(member.ID, true, email, "Google", request, ct);

        await db.SaveChangesAsync(ct);

        logger.LogInformation("{Email} Google 로그인", member.Email);

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

    private async Task LogLoginAttempt(int? memberID, bool success, string account, string? reason, Command request, CancellationToken ct)
    {
        var log = MemberLoginLog.Create(
            memberID,
            success,
            account,
            reason,
            ipAddress: request.IpAddress,
            userAgent: request.UserAgent
        );

        await db.MemberLoginLog.AddAsync(log, ct);
        await db.SaveChangesAsync(ct);
    }
}