using System.IO;
using System.Text.Json;
using System.Text.Json.Serialization;

namespace AxCopilot.Services;

/// <summary>
/// 런처 항목 실행 횟수와 최신성을 추적하여 퍼지 검색 결과 정렬에 활용합니다.
/// 저장 위치: %APPDATA%\AxCopilot\usage.json
/// 점수 = 실행횟수 × 최신성감쇠 (30일 반감기: 30일 전 사용 ≈ 현재의 37%)
/// </summary>
internal static class UsageRankingService
{
    private static readonly string _dataFile = Path.Combine(
        Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
        "AxCopilot", "usage.json");

    private static Dictionary<string, UsageRecord> _data = new(StringComparer.OrdinalIgnoreCase);
    private static bool _loaded = false;
    private static readonly object _lock = new();

    private sealed class UsageRecord
    {
        [JsonPropertyName("c")] public int Count      { get; set; }
        [JsonPropertyName("t")] public long LastUsedMs { get; set; }
    }

    // ─── 공개 API ──────────────────────────────────────────────────────────────

    /// <summary>항목 실행 시 호출하여 카운트 및 최근 실행 시각을 갱신합니다.</summary>
    public static void RecordExecution(string key)
    {
        if (string.IsNullOrWhiteSpace(key)) return;
        EnsureLoaded();
        lock (_lock)
        {
            if (!_data.TryGetValue(key, out var rec))
                rec = _data[key] = new UsageRecord();
            rec.Count++;
            rec.LastUsedMs = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
        }
        _ = SaveAsync();
    }

    /// <summary>실행 횟수 × 최신성 감쇠 합산 점수를 반환합니다. 없으면 0.</summary>
    public static double GetScore(string key)
    {
        if (string.IsNullOrWhiteSpace(key)) return 0;
        EnsureLoaded();
        UsageRecord? rec;
        lock (_lock) { _data.TryGetValue(key, out rec); }
        if (rec == null) return 0;

        // 최신성 감쇠: 30일 반감기
        var daysSince = (DateTimeOffset.UtcNow.ToUnixTimeMilliseconds() - rec.LastUsedMs) / 86_400_000.0;
        var decay = Math.Exp(-daysSince / 43.3); // ln(2)/30 ≈ 1/43.3 → 30일에 0.5배
        return rec.Count * decay;
    }

    /// <summary>실행 횟수 기준 상위 N개의 (경로, 횟수) 목록을 반환합니다.</summary>
    public static IReadOnlyList<(string Path, int Count)> GetTopItems(int n)
    {
        EnsureLoaded();
        lock (_lock)
            return _data
                .OrderByDescending(kv => GetScore(kv.Key))
                .Take(n)
                .Select(kv => (kv.Key, kv.Value.Count))
                .ToList();
    }

    /// <summary>실행 점수(횟수×최신성) 기준으로 내림차순 정렬합니다. 동점이면 원래 순서 유지.</summary>
    public static IEnumerable<T> SortByUsage<T>(IEnumerable<T> items, Func<T, string?> keySelector)
    {
        EnsureLoaded();
        return items
            .Select((item, idx) => (item, idx, score: GetScore(keySelector(item) ?? "")))
            .OrderByDescending(x => x.score)
            .ThenBy(x => x.idx)
            .Select(x => x.item);
    }

    // ─── 내부 ────────────────────────────────────────────────────────────────

    private static void EnsureLoaded()
    {
        if (_loaded) return;
        lock (_lock)
        {
            if (_loaded) return;
            try
            {
                if (File.Exists(_dataFile))
                {
                    var json = File.ReadAllText(_dataFile);
                    // 새 형식(UsageRecord) 먼저 시도
                    var newData = JsonSerializer.Deserialize<Dictionary<string, UsageRecord>>(json);
                    if (newData != null)
                    {
                        _data = new Dictionary<string, UsageRecord>(newData, StringComparer.OrdinalIgnoreCase);
                        _loaded = true;
                        return;
                    }
                }
            }
            catch
            {
                // 구형식 마이그레이션 시도 (키:횟수 형식)
                try
                {
                    if (File.Exists(_dataFile))
                    {
                        var json = File.ReadAllText(_dataFile);
                        var oldData = JsonSerializer.Deserialize<Dictionary<string, int>>(json);
                        if (oldData != null)
                        {
                            var nowMs = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
                            _data = new Dictionary<string, UsageRecord>(StringComparer.OrdinalIgnoreCase);
                            foreach (var kv in oldData)
                                _data[kv.Key] = new UsageRecord { Count = kv.Value, LastUsedMs = nowMs };
                            LogService.Info("[UsageRanking] 구형 usage.json 마이그레이션 완료");
                        }
                    }
                }
                catch (Exception ex)
                {
                    LogService.Warn($"usage.json 로드 실패: {ex.Message}");
                }
            }
            _loaded = true;
        }
    }

    private static async Task SaveAsync()
    {
        try
        {
            Dictionary<string, UsageRecord> snapshot;
            lock (_lock) { snapshot = new Dictionary<string, UsageRecord>(_data); }

            Directory.CreateDirectory(Path.GetDirectoryName(_dataFile)!);
            var json = JsonSerializer.Serialize(snapshot, new JsonSerializerOptions { WriteIndented = false });
            await File.WriteAllTextAsync(_dataFile, json);
        }
        catch (Exception ex)
        {
            LogService.Warn($"usage.json 저장 실패: {ex.Message}");
        }
    }
}
