using System.IO;
using System.Text;

namespace AxCopilot.Services.Agent;

/// <summary>
/// Phase 30-C: CC 동등 4-layer 계층 메모리 통합 조회 서비스.
///
/// 우선순위 (낮→높):
///   1. Managed — %PROGRAMDATA%\AxCopilot\AX.md (전사 공통, IT 관리)
///   2. User    — %APPDATA%\AxCopilot\AX.md + rules\*.md (개인 전역)
///   3. Project — {WorkFolder}\AX.md + .ax\rules\*.md (팀 공유, git 커밋)
///   4. Local   — {WorkFolder}\AX.local.md (개인 프로젝트별, gitignore)
///
/// CC의 getMemoryFiles() + getUserContext()와 동등한 기능을 제공합니다.
/// </summary>
public class HierarchicalMemoryService
{
    /// <summary>메모리 계층 레벨.</summary>
    public enum MemoryLevel { Managed, User, Project, Local }

    /// <summary>로드된 메모리 파일 한 건.</summary>
    public record MemoryFile(MemoryLevel Level, string FilePath, string Content);

    private static readonly string ManagedDir = Path.Combine(
        Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData),
        "AxCopilot");

    private static readonly string UserDir = Path.Combine(
        Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
        "AxCopilot");

    /// <summary>
    /// 4-layer 메모리를 전부 수집하여 반환합니다.
    /// 하위 레벨이 상위 레벨보다 우선합니다.
    /// </summary>
    public IReadOnlyList<MemoryFile> CollectAll(string? workFolder)
    {
        var result = new List<MemoryFile>();

        // Layer 1: Managed
        CollectFromDir(result, MemoryLevel.Managed, ManagedDir);

        // Layer 2: User
        CollectFromDir(result, MemoryLevel.User, UserDir);
        CollectRules(result, MemoryLevel.User, Path.Combine(UserDir, "rules"));

        // Layer 3: Project
        if (!string.IsNullOrEmpty(workFolder) && Directory.Exists(workFolder))
        {
            CollectFromDir(result, MemoryLevel.Project, workFolder);
            CollectRules(result, MemoryLevel.Project, Path.Combine(workFolder, ".ax", "rules"));
        }

        // Layer 4: Local
        if (!string.IsNullOrEmpty(workFolder))
        {
            var localMd = Path.Combine(workFolder, "AX.local.md");
            if (File.Exists(localMd))
                result.Add(new MemoryFile(MemoryLevel.Local, localMd, ReadSafe(localMd)));
        }

        return result;
    }

    /// <summary>
    /// 모든 계층의 메모리를 하나의 문자열로 병합합니다.
    /// 시스템 프롬프트에 직접 삽입할 수 있는 형태입니다.
    /// </summary>
    public string BuildMergedContext(string? workFolder, int maxChars = 40_000)
    {
        var files = CollectAll(workFolder);
        if (files.Count == 0) return string.Empty;

        var sb = new StringBuilder();
        foreach (var file in files)
        {
            if (sb.Length + file.Content.Length > maxChars)
            {
                // 초과 시 잘라서 추가
                var remaining = maxChars - sb.Length;
                if (remaining > 100)
                {
                    sb.AppendLine($"# [{file.Level}] {Path.GetFileName(file.FilePath)} (일부)");
                    sb.AppendLine(file.Content[..remaining]);
                }
                break;
            }

            sb.AppendLine($"# [{file.Level}] {Path.GetFileName(file.FilePath)}");
            sb.AppendLine(file.Content);
            sb.AppendLine();
        }

        return sb.ToString();
    }

    /// <summary>특정 레벨의 메모리만 조회합니다.</summary>
    public IReadOnlyList<MemoryFile> GetByLevel(string? workFolder, MemoryLevel level)
        => CollectAll(workFolder).Where(f => f.Level == level).ToList();

    private static void CollectFromDir(List<MemoryFile> result, MemoryLevel level, string dir)
    {
        var axMd = Path.Combine(dir, "AX.md");
        if (File.Exists(axMd))
            result.Add(new MemoryFile(level, axMd, ReadSafe(axMd)));
    }

    private static void CollectRules(List<MemoryFile> result, MemoryLevel level, string rulesDir)
    {
        if (!Directory.Exists(rulesDir)) return;

        foreach (var mdFile in Directory.GetFiles(rulesDir, "*.md").OrderBy(f => f))
        {
            result.Add(new MemoryFile(level, mdFile, ReadSafe(mdFile)));
        }
    }

    private static string ReadSafe(string path)
    {
        try { return File.ReadAllText(path, Encoding.UTF8); }
        catch (Exception) { return $"[파일 읽기 실패: {path}]"; }
    }
}
