using System.IO;
using System.Text;

namespace AxCopilot.Services.Agent;

/// <summary>규칙 파일의 프론트매터.</summary>
public record RuleFrontmatter
{
    public string Name { get; init; } = "";
    public IReadOnlyList<string> Paths { get; init; } = Array.Empty<string>();
    // 빈 배열 → 항상 적용
    // ["src/**/*.py"] → Python 파일 작업 시만 적용
}

/// <summary>로드된 규칙 파일.</summary>
public record RuleFile
{
    public string FilePath { get; init; } = "";
    public RuleFrontmatter Frontmatter { get; init; } = new();
    public string Body { get; init; } = "";
}

/// <summary>
/// Phase 17-E: .ax/rules/*.md의 paths: 프론트매터 기반 경로 범위 규칙 주입.
/// 현재 작업 파일에 맞는 규칙만 시스템 프롬프트에 주입.
/// </summary>
public class PathScopedRuleInjector
{
    private readonly string _rulesDir;
    private List<RuleFile>? _cachedRules;

    public PathScopedRuleInjector(string projectRoot)
    {
        _rulesDir = Path.Combine(projectRoot, ".ax", "rules");
    }

    /// <summary>규칙 파일 로드/캐시 갱신.</summary>
    public async Task LoadRulesAsync()
    {
        if (!Directory.Exists(_rulesDir))
        {
            _cachedRules = new List<RuleFile>();
            return;
        }

        var rules = new List<RuleFile>();
        foreach (var file in Directory.GetFiles(_rulesDir, "*.md"))
        {
            try
            {
                var rule = await ParseRuleFileAsync(file);
                if (rule != null) rules.Add(rule);
            }
            catch (Exception) { }
        }
        _cachedRules = rules;
    }

    /// <summary>현재 파일 경로에 맞는 규칙 반환.</summary>
    public IReadOnlyList<RuleFile> GetActiveRulesForFile(string currentFilePath)
    {
        if (_cachedRules == null) return new List<RuleFile>();
        var normalized = currentFilePath.Replace('\\', '/');
        return _cachedRules.Where(r =>
            r.Frontmatter.Paths.Count == 0 || // paths 없으면 항상 적용
            r.Frontmatter.Paths.Any(p => GlobMatcher.IsMatch(normalized, p))).ToList();
    }

    /// <summary>모든 규칙을 로드 (경로 필터 없이 전체 적용 — 비경로 규칙용).</summary>
    public IReadOnlyList<RuleFile> GetGlobalRules()
        => (_cachedRules ?? new List<RuleFile>())
            .Where(r => r.Frontmatter.Paths.Count == 0).ToList();

    /// <summary>시스템 프롬프트 주입용 텍스트 빌드.</summary>
    public string BuildInjection(IReadOnlyList<RuleFile> rules)
    {
        if (rules.Count == 0) return string.Empty;
        var sb = new StringBuilder("## 프로젝트 규칙\n\n");
        foreach (var rule in rules)
        {
            if (!string.IsNullOrEmpty(rule.Frontmatter.Name))
                sb.AppendLine($"### {rule.Frontmatter.Name}");
            sb.AppendLine(rule.Body);
            sb.AppendLine();
        }
        return sb.ToString();
    }

    /// <summary>규칙 파일의 크기 경고 여부 확인.</summary>
    public IReadOnlyList<string> GetOversizedRuleFiles()
        => (_cachedRules ?? new List<RuleFile>())
            .Where(r => r.Body.Length > AxMdIncludeResolver.OversizeWarningChars)
            .Select(r => Path.GetFileName(r.FilePath))
            .ToList();

    private static async Task<RuleFile?> ParseRuleFileAsync(string filePath)
    {
        var content = await File.ReadAllTextAsync(filePath, Encoding.UTF8);

        string frontmatterText = "";
        string body = content;

        if (content.TrimStart().StartsWith("---"))
        {
            var first = content.IndexOf("---", StringComparison.Ordinal);
            var second = content.IndexOf("---", first + 3, StringComparison.Ordinal);
            if (second > 0)
            {
                frontmatterText = content[(first + 3)..second].Trim();
                body = content[(second + 3)..].Trim();
            }
        }

        var frontmatter = ParseFrontmatter(frontmatterText);
        return new RuleFile
        {
            FilePath = filePath,
            Frontmatter = frontmatter,
            Body = body
        };
    }

    private static RuleFrontmatter ParseFrontmatter(string text)
    {
        var name = "";
        var paths = new List<string>();
        bool inPaths = false;

        foreach (var line in text.Split('\n'))
        {
            var trimmed = line.TrimEnd('\r');
            var ci = trimmed.IndexOf(':');

            if (inPaths)
            {
                if (trimmed.TrimStart().StartsWith("-"))
                {
                    var pat = trimmed.TrimStart().TrimStart('-').Trim().Trim('"', '\'');
                    if (!string.IsNullOrEmpty(pat)) paths.Add(pat);
                    continue;
                }
                else if (!string.IsNullOrWhiteSpace(trimmed) && ci > 0)
                {
                    inPaths = false;
                }
            }

            if (ci <= 0) continue;
            var key = trimmed[..ci].Trim().ToLowerInvariant();
            var value = trimmed[(ci + 1)..].Trim().Trim('"', '\'');

            switch (key)
            {
                case "name": name = value; break;
                case "paths":
                    if (!string.IsNullOrEmpty(value)) paths.Add(value);
                    else inPaths = true;
                    break;
            }
        }

        return new RuleFrontmatter { Name = name, Paths = paths };
    }
}
