using System.Text.Json;
using Application.Abstractions.YouTube;
using Microsoft.Extensions.Logging;

namespace Infrastructure.YouTube;

/// <summary>
/// YouTube Data API v3 — 채널 정보, 구독/멤버십 확인
/// API Key(공개 정보)와 OAuth Access Token(사용자별)을 분리하여 사용
/// </summary>
internal sealed class YouTubeApiService(
    IHttpClientFactory httpClientFactory,
    ILogger<YouTubeApiService> logger
) : IYouTubeApiService
{
    private const string BaseUrl = "https://www.googleapis.com/youtube/v3";

    // ── 공개 정보 (API Key) ──────────────────────────────────────────

    public async Task<YouTubeChannelInfo?> GetChannelByIdAsync(string channelId, CancellationToken ct)
    {
        var url = $"{BaseUrl}/channels?part=snippet,statistics,brandingSettings&id={Uri.EscapeDataString(channelId)}";
        return await FetchChannelAsync(url, ct);
    }

    public async Task<YouTubeChannelInfo?> GetChannelByHandleAsync(string handle, CancellationToken ct)
    {
        var cleanHandle = handle.TrimStart('@');
        var url = $"{BaseUrl}/channels?part=snippet,statistics,brandingSettings&forHandle={Uri.EscapeDataString(cleanHandle)}";
        return await FetchChannelAsync(url, ct);
    }

    public async Task<IReadOnlyList<YouTubeChannelInfo>> GetChannelsByIdsAsync(IReadOnlyList<string> channelIds, CancellationToken ct)
    {
        if (channelIds.Count == 0)
        {
            return [];
        }

        try
        {
            var ids = string.Join(",", channelIds.Select(Uri.EscapeDataString));
            var client = httpClientFactory.CreateClient("YouTubeApi");
            var url = $"{BaseUrl}/channels?part=snippet,statistics,brandingSettings&id={ids}";
            var response = await client.GetAsync(url, ct);

            if (!response.IsSuccessStatusCode)
            {
                var errorBody = await response.Content.ReadAsStringAsync(ct);
                logger.LogWarning("[YouTube] channels.list (batch) failed: {StatusCode} — {Body}", response.StatusCode, errorBody);
                return [];
            }

            var json = await response.Content.ReadAsStringAsync(ct);
            var doc = JsonSerializer.Deserialize<JsonElement>(json);
            var items = doc.GetProperty("items");

            return items.EnumerateArray().Select(ParseChannelInfo).ToList();
        }
        catch (Exception ex)
        {
            logger.LogError(ex, "[YouTube] GetChannelsByIds error");
            return [];
        }
    }

    public async Task<YouTubeLiveStreamInfo?> GetLiveStreamInfoAsync(string videoId, CancellationToken ct)
    {
        try
        {
            var client = httpClientFactory.CreateClient("YouTubeApi");
            var url = $"{BaseUrl}/videos?part=snippet,liveStreamingDetails&id={Uri.EscapeDataString(videoId)}";
            var response = await client.GetAsync(url, ct);

            if (!response.IsSuccessStatusCode)
            {
                logger.LogWarning("[YouTube] videos.list failed: {StatusCode}", response.StatusCode);
                return null;
            }

            var json = await response.Content.ReadAsStringAsync(ct);
            var doc = JsonSerializer.Deserialize<JsonElement>(json);
            var items = doc.GetProperty("items");

            if (items.GetArrayLength() == 0)
            {
                return null;
            }

            var item = items[0];
            var snippet = item.GetProperty("snippet");

            var liveBroadcastContent = snippet.TryGetProperty("liveBroadcastContent", out var lbcProp)
                ? lbcProp.GetString() ?? "none"
                : "none";

            string? activeLiveChatId = null;
            DateTime? scheduledStart = null;
            DateTime? actualStart = null;

            if (item.TryGetProperty("liveStreamingDetails", out var lsd))
            {
                activeLiveChatId = lsd.TryGetProperty("activeLiveChatId", out var chatIdProp)
                    ? chatIdProp.GetString()
                    : null;

                scheduledStart = lsd.TryGetProperty("scheduledStartTime", out var ssProp)
                    ? DateTime.TryParse(ssProp.GetString(), out var ss) ? ss : null
                    : null;

                actualStart = lsd.TryGetProperty("actualStartTime", out var asProp)
                    ? DateTime.TryParse(asProp.GetString(), out var ast) ? ast : null
                    : null;
            }

            return new YouTubeLiveStreamInfo(
                videoId,
                snippet.GetProperty("title").GetString() ?? "",
                snippet.GetProperty("channelId").GetString() ?? "",
                liveBroadcastContent,
                activeLiveChatId,
                scheduledStart,
                actualStart
            );
        }
        catch (Exception ex)
        {
            logger.LogError(ex, "[YouTube] GetLiveStreamInfo error for videoId={VideoId}", videoId);
            return null;
        }
    }

    // ── 사용자별 (OAuth Access Token) ────────────────────────────────

    public async Task<bool> IsSubscribedAsync(string accessToken, string channelId, CancellationToken ct)
    {
        try
        {
            var client = CreateAuthClient(accessToken);
            var url = $"{BaseUrl}/subscriptions?part=id&mine=true&forChannelId={Uri.EscapeDataString(channelId)}";
            var response = await client.GetAsync(url, ct);

            if (!response.IsSuccessStatusCode)
            {
                logger.LogWarning("[YouTube] subscriptions.list failed: {StatusCode}", response.StatusCode);
                return false;
            }

            var json = await response.Content.ReadAsStringAsync(ct);
            var doc = JsonSerializer.Deserialize<JsonElement>(json);
            var totalResults = doc.GetProperty("pageInfo").GetProperty("totalResults").GetInt32();

            return totalResults > 0;
        }
        catch (Exception ex)
        {
            logger.LogError(ex, "[YouTube] IsSubscribed error for channelId={ChannelId}", channelId);
            return false;
        }
    }

    public async Task<YouTubeMembershipStatus> CheckMembershipAsync(string accessToken, string channelId, CancellationToken ct)
    {
        try
        {
            // YouTube Members API는 채널 소유자만 호출 가능
            // 대안: 사용자의 membershipsLevels 조회 또는 채널 소유자 토큰으로 members.list 호출
            // 여기서는 사용자 관점에서 "내 멤버십" 조회를 시도
            var client = CreateAuthClient(accessToken);
            var url = $"{BaseUrl}/members?part=snippet&mode=list_members&maxResults=1&filterByMemberChannelId={Uri.EscapeDataString(channelId)}";
            var response = await client.GetAsync(url, ct);

            if (!response.IsSuccessStatusCode)
            {
                // 403 = 채널 소유자가 아님 → 멤버십 확인 불가
                if ((int)response.StatusCode == 403)
                {
                    logger.LogInformation("[YouTube] Members API requires channel owner token for channelId={ChannelId}", channelId);
                    return new YouTubeMembershipStatus(false, null, null);
                }

                logger.LogWarning("[YouTube] members.list failed: {StatusCode}", response.StatusCode);
                return new YouTubeMembershipStatus(false, null, null);
            }

            var json = await response.Content.ReadAsStringAsync(ct);
            var doc = JsonSerializer.Deserialize<JsonElement>(json);
            var items = doc.GetProperty("items");

            if (items.GetArrayLength() == 0)
            {
                return new YouTubeMembershipStatus(false, null, null);
            }

            var snippet = items[0].GetProperty("snippet");
            var memberLevel = snippet.TryGetProperty("membershipsDetails", out var md)
                && md.TryGetProperty("highestAccessibleLevel", out var level)
                    ? level.GetString()
                    : null;

            var memberSince = snippet.TryGetProperty("memberSince", out var msProp)
                ? DateTime.TryParse(msProp.GetString(), out var ms) ? ms : (DateTime?)null
                : null;

            return new YouTubeMembershipStatus(true, memberLevel, memberSince);
        }
        catch (Exception ex)
        {
            logger.LogError(ex, "[YouTube] CheckMembership error for channelId={ChannelId}", channelId);
            return new YouTubeMembershipStatus(false, null, null);
        }
    }

    public async Task<YouTubeChannelInfo?> GetMyChannelAsync(string accessToken, CancellationToken ct)
    {
        try
        {
            var client = CreateAuthClient(accessToken);
            var url = $"{BaseUrl}/channels?part=snippet,statistics,brandingSettings&mine=true";
            var response = await client.GetAsync(url, ct);

            if (!response.IsSuccessStatusCode)
            {
                var errorBody = await response.Content.ReadAsStringAsync(ct);
                logger.LogWarning("[YouTube] GetMyChannel failed: {StatusCode} — {Body}", response.StatusCode, errorBody);
                return null;
            }

            var json = await response.Content.ReadAsStringAsync(ct);
            var doc = JsonSerializer.Deserialize<JsonElement>(json);
            var items = doc.GetProperty("items");

            if (items.GetArrayLength() == 0)
            {
                logger.LogWarning("[YouTube] GetMyChannel: no channel found for authenticated user");
                return null;
            }

            return ParseChannelInfo(items[0]);
        }
        catch (Exception ex)
        {
            logger.LogError(ex, "[YouTube] GetMyChannel error");
            return null;
        }
    }

    // ── Private ──────────────────────────────────────────────────────

    private async Task<YouTubeChannelInfo?> FetchChannelAsync(string url, CancellationToken ct)
    {
        try
        {
            var client = httpClientFactory.CreateClient("YouTubeApi");
            var response = await client.GetAsync(url, ct);

            if (!response.IsSuccessStatusCode)
            {
                var errorBody = await response.Content.ReadAsStringAsync(ct);
                logger.LogWarning("[YouTube] channels.list failed: {StatusCode} — {Body}", response.StatusCode, errorBody);
                return null;
            }

            var json = await response.Content.ReadAsStringAsync(ct);
            var doc = JsonSerializer.Deserialize<JsonElement>(json);
            var items = doc.GetProperty("items");

            if (items.GetArrayLength() == 0)
            {
                return null;
            }

            var item = items[0];
            return ParseChannelInfo(item);
        }
        catch (Exception ex)
        {
            logger.LogError(ex, "[YouTube] FetchChannel error");
            return null;
        }
    }

    private static YouTubeChannelInfo ParseChannelInfo(JsonElement item)
    {
        var snippet = item.GetProperty("snippet");
        var statistics = item.GetProperty("statistics");

        // brandingSettings: 배너 + 이메일
        string? bannerUrl = null;
        string? email = null;
        if (item.TryGetProperty("brandingSettings", out var bs))
        {
            if (bs.TryGetProperty("image", out var img)
                && img.TryGetProperty("bannerExternalUrl", out var bannerProp))
            {
                bannerUrl = bannerProp.GetString();
            }

            if (bs.TryGetProperty("channel", out var bsCh)
                && bsCh.TryGetProperty("email", out var emailProp))
            {
                email = emailProp.GetString();
            }
        }

        // snippet.publishedAt: YouTube 채널 가입일
        DateTime? publishedAt = null;
        if (snippet.TryGetProperty("publishedAt", out var paProp)
            && DateTime.TryParse(paProp.GetString(), out var pa))
        {
            publishedAt = pa;
        }

        return new YouTubeChannelInfo(
            item.GetProperty("id").GetString()!,
            snippet.GetProperty("title").GetString() ?? "",
            snippet.TryGetProperty("description", out var descProp) ? descProp.GetString() ?? "" : "",
            snippet.GetProperty("thumbnails").GetProperty("default").GetProperty("url").GetString() ?? "",
            bannerUrl,
            snippet.TryGetProperty("customUrl", out var cuProp) ? cuProp.GetString() : null,
            statistics.TryGetProperty("subscriberCount", out var scProp) && long.TryParse(scProp.GetString(), out var sc) ? sc : 0,
            statistics.TryGetProperty("videoCount", out var vcProp) && long.TryParse(vcProp.GetString(), out var vc) ? vc : 0,
            statistics.TryGetProperty("viewCount", out var vwProp) && long.TryParse(vwProp.GetString(), out var vw) ? vw : 0,
            email,
            publishedAt
        );
    }

    private HttpClient CreateAuthClient(string accessToken)
    {
        var client = httpClientFactory.CreateClient("YouTubeApi");
        client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", accessToken);
        return client;
    }
}