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

namespace AxCopilot.Services;

/// <summary>
/// Phase L3-5: 파일 태그 시스템 서비스.
/// 파일·폴더에 사용자 정의 태그를 부여하고 태그 기반 검색을 지원합니다.
/// 데이터는 %APPDATA%\AxCopilot\file_tags.json에 저장됩니다.
/// </summary>
public class FileTagService
{
    private static readonly string TagFile = Path.Combine(
        Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
        "AxCopilot", "file_tags.json");

    private static readonly JsonSerializerOptions JsonOpts = new()
    {
        WriteIndented = true,
        PropertyNameCaseInsensitive = true,
    };

    /// <summary>key = 정규화된 파일 경로, value = 태그 집합</summary>
    private Dictionary<string, HashSet<string>> _data =
        new(StringComparer.OrdinalIgnoreCase);

    private bool _loaded;

    // ─── 싱글턴 ─────────────────────────────────────────────────────────────
    private static FileTagService? _instance;
    public static FileTagService Instance => _instance ??= new FileTagService();
    private FileTagService() { }

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

    /// <summary>파일에 태그를 추가합니다.</summary>
    public void AddTag(string path, string tag)
    {
        EnsureLoaded();
        path = NormalizePath(path);
        tag  = NormalizeTag(tag);
        if (string.IsNullOrEmpty(path) || string.IsNullOrEmpty(tag)) return;

        if (!_data.TryGetValue(path, out var tags))
        {
            tags = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
            _data[path] = tags;
        }
        tags.Add(tag);
        Save();
    }

    /// <summary>파일에서 태그를 제거합니다.</summary>
    public void RemoveTag(string path, string tag)
    {
        EnsureLoaded();
        path = NormalizePath(path);
        tag  = NormalizeTag(tag);
        if (!_data.TryGetValue(path, out var tags)) return;
        tags.Remove(tag);
        if (tags.Count == 0) _data.Remove(path);
        Save();
    }

    /// <summary>파일의 모든 태그를 제거합니다.</summary>
    public void ClearTags(string path)
    {
        EnsureLoaded();
        path = NormalizePath(path);
        _data.Remove(path);
        Save();
    }

    /// <summary>파일의 태그 목록을 반환합니다.</summary>
    public IReadOnlyList<string> GetTags(string path)
    {
        EnsureLoaded();
        path = NormalizePath(path);
        return _data.TryGetValue(path, out var tags)
            ? tags.OrderBy(t => t).ToList()
            : Array.Empty<string>();
    }

    /// <summary>특정 태그가 부여된 파일 경로 목록을 반환합니다.</summary>
    public IReadOnlyList<string> GetFilesByTag(string tag)
    {
        EnsureLoaded();
        tag = NormalizeTag(tag);
        return _data
            .Where(kv => kv.Value.Contains(tag))
            .Select(kv => kv.Key)
            .OrderBy(p => p)
            .ToList();
    }

    /// <summary>등록된 모든 태그와 각 파일 수를 반환합니다.</summary>
    public IReadOnlyDictionary<string, int> GetAllTags()
    {
        EnsureLoaded();
        return _data
            .SelectMany(kv => kv.Value)
            .GroupBy(t => t, StringComparer.OrdinalIgnoreCase)
            .ToDictionary(g => g.Key, g => g.Count());
    }

    /// <summary>경로에 태그가 하나 이상 있는지 확인합니다.</summary>
    public bool HasTags(string path)
    {
        EnsureLoaded();
        path = NormalizePath(path);
        return _data.TryGetValue(path, out var tags) && tags.Count > 0;
    }

    // ─── 내부 헬퍼 ──────────────────────────────────────────────────────────

    private void EnsureLoaded()
    {
        if (_loaded) return;
        _loaded = true;
        try
        {
            if (!File.Exists(TagFile)) return;
            var raw = JsonSerializer.Deserialize<Dictionary<string, List<string>>>(
                File.ReadAllText(TagFile), JsonOpts);
            if (raw != null)
            {
                _data = raw.ToDictionary(
                    kv => kv.Key,
                    kv => new HashSet<string>(kv.Value, StringComparer.OrdinalIgnoreCase),
                    StringComparer.OrdinalIgnoreCase);
            }
        }
        catch (Exception ex) { LogService.Warn($"[FileTagService] 로드 실패: {ex.Message}"); }
    }

    private void Save()
    {
        try
        {
            Directory.CreateDirectory(Path.GetDirectoryName(TagFile)!);
            var raw = _data.ToDictionary(
                kv => kv.Key,
                kv => kv.Value.OrderBy(t => t).ToList());
            File.WriteAllText(TagFile, JsonSerializer.Serialize(raw, JsonOpts));
        }
        catch (Exception ex) { LogService.Warn($"[FileTagService] 저장 실패: {ex.Message}"); }
    }

    private static string NormalizePath(string path)
        => path.Trim().TrimEnd('\\', '/');

    private static string NormalizeTag(string tag)
        => tag.Trim().ToLowerInvariant().Replace(" ", "-");
}
