using System.Text.Json;
using Application.Abstractions.YouTube;
using StackExchange.Redis;

namespace Infrastructure.YouTube;

/// <summary>
/// Redis Hash 기반 라이브 상태 저장소
/// Key: "youtube:live:channels" (Hash — channelId → LiveStreamInfo JSON)
/// PubSub 알림이 없으면 데이터가 없음 = 라이브 아님
/// </summary>
internal sealed class YouTubeLiveStateStore(IConnectionMultiplexer redis) : IYouTubeLiveStateStore
{
    private const string HashKey = "youtube:live:channels";
    private static readonly TimeSpan MaxLiveDuration = TimeSpan.FromHours(12); // 안전장치: 12시간 후 자동 만료

    public async Task SetLiveAsync(string channelId, YouTubeLiveStreamInfo liveInfo)
    {
        var db = redis.GetDatabase();
        var json = JsonSerializer.Serialize(liveInfo);
        await db.HashSetAsync(HashKey, channelId, json);

        // 전체 Hash에 만료 설정 (최소 12시간은 유지)
        var ttl = await db.KeyTimeToLiveAsync(HashKey);
        if (ttl is null || ttl < MaxLiveDuration)
        {
            await db.KeyExpireAsync(HashKey, MaxLiveDuration);
        }
    }

    public async Task ClearLiveAsync(string channelId)
    {
        var db = redis.GetDatabase();
        await db.HashDeleteAsync(HashKey, channelId);
    }

    public async Task<YouTubeLiveStreamInfo?> GetLiveAsync(string channelId)
    {
        var db = redis.GetDatabase();
        var json = await db.HashGetAsync(HashKey, channelId);

        if (json.IsNullOrEmpty)
        {
            return null;
        }

        return JsonSerializer.Deserialize<YouTubeLiveStreamInfo>(json.ToString());
    }

    public async Task<IReadOnlyList<YouTubeLiveStreamInfo>> GetAllLiveAsync()
    {
        var db = redis.GetDatabase();
        var entries = await db.HashGetAllAsync(HashKey);

        var result = new List<YouTubeLiveStreamInfo>();
        foreach (var entry in entries)
        {
            var info = JsonSerializer.Deserialize<YouTubeLiveStreamInfo>(entry.Value.ToString());
            if (info is not null)
            {
                result.Add(info);
            }
        }

        return result.AsReadOnly();
    }
}