using System.Security.Cryptography;
using System.Text;
using Microsoft.EntityFrameworkCore;
using DramaLing.Api.Data;
using DramaLing.Api.Models.Entities;
using DramaLing.Api.Models.Dtos;

namespace DramaLing.Api.Services;

public interface IAudioCacheService
{
    Task<TTSResponse> GetOrCreateAudioAsync(TTSRequest request);
    Task<string> GenerateCacheKeyAsync(string text, string accent, string voice);
    Task UpdateAccessTimeAsync(string cacheKey);
    Task CleanupOldCacheAsync();
}

public class AudioCacheService : IAudioCacheService
{
    private readonly DramaLingDbContext _context;
    private readonly IAzureSpeechService _speechService;
    private readonly ILogger<AudioCacheService> _logger;

    public AudioCacheService(
        DramaLingDbContext context,
        IAzureSpeechService speechService,
        ILogger<AudioCacheService> logger)
    {
        _context = context;
        _speechService = speechService;
        _logger = logger;
    }

    public async Task<TTSResponse> GetOrCreateAudioAsync(TTSRequest request)
    {
        try
        {
            var cacheKey = await GenerateCacheKeyAsync(request.Text, request.Accent, request.Voice);

            // 檢查快取
            var cachedAudio = await _context.AudioCaches
                .FirstOrDefaultAsync(a => a.TextHash == cacheKey);

            if (cachedAudio != null)
            {
                // 更新訪問時間
                await UpdateAccessTimeAsync(cacheKey);

                return new TTSResponse
                {
                    AudioUrl = cachedAudio.AudioUrl,
                    Duration = cachedAudio.DurationMs.HasValue ? cachedAudio.DurationMs.Value / 1000.0f : 0,
                    CacheHit = true
                };
            }

            // 生成新音頻
            var response = await _speechService.GenerateAudioAsync(request);

            if (!string.IsNullOrEmpty(response.Error))
            {
                return response;
            }

            // 存入快取
            var audioCache = new AudioCache
            {
                TextHash = cacheKey,
                TextContent = request.Text,
                Accent = request.Accent,
                VoiceId = request.Voice,
                AudioUrl = response.AudioUrl,
                DurationMs = (int)(response.Duration * 1000),
                CreatedAt = DateTime.UtcNow,
                LastAccessed = DateTime.UtcNow,
                AccessCount = 1
            };

            _context.AudioCaches.Add(audioCache);
            await _context.SaveChangesAsync();

            _logger.LogInformation("Created new audio cache entry for text: {Text}", request.Text);

            return response;
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error in GetOrCreateAudioAsync for text: {Text}", request.Text);
            return new TTSResponse
            {
                Error = "Internal error processing audio request"
            };
        }
    }

    public async Task<string> GenerateCacheKeyAsync(string text, string accent, string voice)
    {
        var combined = $"{text}|{accent}|{voice}";
        using var sha256 = SHA256.Create();
        var hash = sha256.ComputeHash(Encoding.UTF8.GetBytes(combined));
        return Convert.ToHexString(hash).ToLowerInvariant();
    }

    public async Task UpdateAccessTimeAsync(string cacheKey)
    {
        try
        {
            var audioCache = await _context.AudioCaches
                .FirstOrDefaultAsync(a => a.TextHash == cacheKey);

            if (audioCache != null)
            {
                audioCache.LastAccessed = DateTime.UtcNow;
                audioCache.AccessCount++;
                await _context.SaveChangesAsync();
            }
        }
        catch (Exception ex)
        {
            _logger.LogWarning(ex, "Failed to update access time for cache key: {CacheKey}", cacheKey);
        }
    }

    public async Task CleanupOldCacheAsync()
    {
        try
        {
            var cutoffDate = DateTime.UtcNow.AddDays(-30);

            var oldEntries = await _context.AudioCaches
                .Where(a => a.LastAccessed < cutoffDate)
                .ToListAsync();

            if (oldEntries.Any())
            {
                _context.AudioCaches.RemoveRange(oldEntries);
                await _context.SaveChangesAsync();

                _logger.LogInformation("Cleaned up {Count} old audio cache entries", oldEntries.Count);
            }
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error during audio cache cleanup");
        }
    }
}