using Application.Abstractions.Data;
using Application.Abstractions.Messaging;
using Application.Abstractions.YouTube;
using Domain.Entities.Members;
using Microsoft.EntityFrameworkCore;

namespace Application.Features.Api.Studio.ConnectYouTube;

internal sealed class Handler(
    IAppDbContext db,
    IGoogleOAuthService oauthService,
    IYouTubeApiService youtubeApiService,
    IYouTubeChannelCache channelCache
) : ICommandHandler<Command>
{
    public async Task Handle(Command request, CancellationToken ct)
    {
        var config = await db.Config.AsNoTracking().OrderByDescending(x => x.ID).FirstOrDefaultAsync(ct);

        if (config is null
            || string.IsNullOrEmpty(config.External.GoogleClientId)
            || string.IsNullOrEmpty(config.External.GoogleClientSecretEnc
        )) {
            throw new InvalidOperationException("Google OAuth 설정이 없습니다.");
        }

        var tokens = await oauthService.ExchangeCodeAsync(
            request.Code,
            request.RedirectUri,
            config.External.GoogleClientId,
            config.External.GoogleClientSecretEnc,
            ct
        );

        if (tokens is null)
        {
            throw new InvalidOperationException("Google 인증 코드 교환에 실패했습니다.");
        }

        var ytInfo = await youtubeApiService.GetMyChannelAsync(tokens.AccessToken, ct);

        if (ytInfo is null)
        {
            throw new InvalidOperationException("YouTube 채널 정보를 가져올 수 없습니다.");
        }

        var youtubeUrl = ytInfo.CustomUrl is not null
            ? $"https://youtube.com/{ytInfo.CustomUrl}"
            : $"https://youtube.com/channel/{ytInfo.ChannelID}";

        // Member 크리에이터 전환
        var member = await db.Member.FirstAsync(m => m.ID == request.MemberID, ct);
        if (!member.IsCreator)
        {
            member.SetCreator(true);
        }

        // Channel 생성 또는 업데이트
        var channel = await db.Channel.FirstOrDefaultAsync(c => c.MemberID == request.MemberID, ct);

        if (channel is null)
        {
            channel = Domain.Entities.Members.Channel.Create(request.MemberID, ytInfo.ChannelID, ytInfo.Title, youtubeUrl);
            db.Channel.Add(channel);
        }

        // YouTube 상세 정보 DB 저장
        channel.UpdateYouTubeInfo(
            ytInfo.ChannelID,
            ytInfo.Title,
            ytInfo.CustomUrl,
            ytInfo.Description,
            ytInfo.ThumbnailUrl,
            ytInfo.BannerUrl,
            ytInfo.SubscriberCount,
            ytInfo.VideoCount,
            ytInfo.ViewCount,
            ytInfo.Email,
            ytInfo.PublishedAt
        );

        // MemberOAuthToken 생성 또는 업데이트 (Provider = "YouTube")
        var oauthToken = await db.MemberOAuthToken.FirstOrDefaultAsync(t => t.MemberID == request.MemberID && t.Provider == "YouTube", ct);

        var scopesStr = string.Join(" ", tokens.Scopes);

        if (oauthToken is null)
        {
            oauthToken = MemberOAuthToken.Create(
                request.MemberID,
                "YouTube",
                tokens.AccessToken,
                tokens.RefreshToken,
                tokens.ExpiresAt,
                scopesStr
            );

            db.MemberOAuthToken.Add(oauthToken);
        }
        else
        {
            oauthToken.UpdateTokens(tokens.AccessToken, tokens.RefreshToken, tokens.ExpiresAt, scopesStr);
        }

        await db.SaveChangesAsync(ct);

        // 캐시 갱신
        await channelCache.SetAsync(ytInfo);
    }
}