using AxCopilot.Models;

namespace AxCopilot.Services.Agent;

/// <summary>
/// Phase 17-E: AgentLoopService — 계층 메모리/컨텍스트 고도화 통합.
/// · 4-layer 계층 메모리 (Managed→User→Project→Local) 시스템 메시지 주입
/// · @include 지시어 재귀 해석 (최대 5단계, 순환 참조 감지)
/// · .ax/rules/*.md paths: 프론트매터 기반 경로 범위 규칙 주입
/// </summary>
public partial class AgentLoopService
{
    // 세션 공유 서비스 인스턴스 (상태 없음 → 정적)
    private static readonly HierarchicalMemoryService _hierarchicalMemory = new();
    private static readonly AxMdIncludeResolver _axMdResolver = new();

    // workFolder별 PathScopedRuleInjector 캐시 (rules 폴더 로드 비용 절감)
    private PathScopedRuleInjector? _pathRuleInjector;
    private string? _pathRuleInjectorFolder;

    // 시스템 메시지 내 마커 (in-place 교체, 중복 방지)
    private const string MemoryMarker    = "## 프로젝트 메모리 (계층 컨텍스트)";
    private const string PathRulesMarker = "## 현재 파일에 적용된 경로 규칙";

    // ─────────────────────────────────────────────────────────────────────
    //  세션 시작 시 계층 메모리 주입
    // ─────────────────────────────────────────────────────────────────────

    /// <summary>
    /// 4-layer 계층 메모리(AX.md 파일군)를 수집하고 @include 지시어를 재귀 해석하여
    /// 시스템 메시지에 주입합니다. 세션 시작 시 한 번만 호출합니다.
    /// </summary>
    internal async Task InjectHierarchicalMemoryAsync(
        List<ChatMessage> messages, string? workFolder, CancellationToken ct)
    {
        if (!(_settings.Settings.Llm.EnableMemorySystem)) return;

        try
        {
            // 4-layer 메모리 병합 문자열 생성
            var merged = _hierarchicalMemory.BuildMergedContext(workFolder);
            if (string.IsNullOrWhiteSpace(merged)) return;

            // @include 지시어 재귀 해석
            var basePath = string.IsNullOrWhiteSpace(workFolder)
                ? Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData)
                : workFolder;

            var resolved = await _axMdResolver.ResolveAsync(merged, basePath);

            if (string.IsNullOrWhiteSpace(resolved)) return;

            // 크기 경고 (40,000자 초과 시)
            if (AxMdIncludeResolver.IsOversized(resolved))
                LogService.Warn($"[Memory] 계층 메모리 컨텍스트가 40,000자를 초과합니다 ({resolved.Length:N0}자).");

            var injection = $"{MemoryMarker}\n\n{resolved.TrimEnd()}";

            // 시스템 메시지에 in-place 삽입 (마커 기반 중복 방지)
            var sysMsg = messages.FirstOrDefault(m => m.Role == "system");
            if (sysMsg != null)
            {
                var idx = sysMsg.Content.IndexOf(MemoryMarker, StringComparison.Ordinal);
                sysMsg.Content = idx >= 0
                    ? sysMsg.Content[..idx] + injection
                    : sysMsg.Content + "\n\n" + injection;
            }
            else
            {
                messages.Insert(0, new ChatMessage { Role = "system", Content = injection });
            }

            var fileCount = _hierarchicalMemory.CollectAll(workFolder).Count;
            EmitEvent(AgentEventType.Thinking, "memory",
                $"계층 메모리 주입됨: {fileCount}개 파일, {resolved.Length:N0}자");

            _ = _eventLog?.AppendAsync(AgentEventLogType.SkillActivated,
                System.Text.Json.JsonSerializer.Serialize(new
                {
                    type       = "hierarchical_memory",
                    fileCount,
                    charCount  = resolved.Length,
                    workFolder = workFolder ?? ""
                }));
        }
        catch (OperationCanceledException) { /* 취소 시 무시 */ }
        catch (Exception ex)
        {
            LogService.Warn($"[Memory] 계층 메모리 주입 실패: {ex.Message}");
        }
    }

    // ─────────────────────────────────────────────────────────────────────
    //  파일 도구 호출 후 경로 범위 규칙 주입
    // ─────────────────────────────────────────────────────────────────────

    /// <summary>
    /// 파일 도구 실행 후, 해당 파일에 적용 가능한 .ax/rules/*.md 경로 범위 규칙을
    /// 시스템 메시지에 주입합니다. 마커 기반 in-place 교체로 중복을 방지합니다.
    /// </summary>
    internal async Task InjectPathScopedRulesAsync(
        string? filePath, List<ChatMessage> messages, CancellationToken ct)
    {
        if (!(_settings.Settings.Llm.EnableMemorySystem)) return;
        if (string.IsNullOrWhiteSpace(filePath)) return;

        var workFolder = _settings.Settings.Llm.WorkFolder;
        if (string.IsNullOrWhiteSpace(workFolder)) return;

        try
        {
            // workFolder 변경 시 injector 재생성 및 규칙 재로드
            if (_pathRuleInjector == null || _pathRuleInjectorFolder != workFolder)
            {
                _pathRuleInjector = new PathScopedRuleInjector(workFolder);
                _pathRuleInjectorFolder = workFolder;
                await _pathRuleInjector.LoadRulesAsync();
            }

            var activeRules = _pathRuleInjector.GetActiveRulesForFile(filePath);
            if (activeRules.Count == 0) return;

            var injection = $"{PathRulesMarker}\n\n{_pathRuleInjector.BuildInjection(activeRules).TrimEnd()}";

            // 시스템 메시지에 in-place 삽입 (마커 기반 중복 방지)
            var sysMsg = messages.FirstOrDefault(m => m.Role == "system");
            if (sysMsg != null)
            {
                var idx = sysMsg.Content.IndexOf(PathRulesMarker, StringComparison.Ordinal);
                sysMsg.Content = idx >= 0
                    ? sysMsg.Content[..idx] + injection
                    : sysMsg.Content + "\n\n" + injection;
            }
            else
            {
                messages.Insert(0, new ChatMessage { Role = "system", Content = injection });
            }

            var ruleNames = string.Join(", ", activeRules
                .Select(r => string.IsNullOrEmpty(r.Frontmatter.Name)
                    ? System.IO.Path.GetFileName(r.FilePath)
                    : r.Frontmatter.Name));

            EmitEvent(AgentEventType.Thinking, "path_rules",
                $"경로 규칙 주입됨: {activeRules.Count}개 ({ruleNames})");

            _ = _eventLog?.AppendAsync(AgentEventLogType.SkillActivated,
                System.Text.Json.JsonSerializer.Serialize(new
                {
                    type      = "path_scoped_rules",
                    filePath,
                    ruleCount = activeRules.Count,
                    ruleNames
                }));
        }
        catch (OperationCanceledException) { /* 취소 시 무시 */ }
        catch (Exception ex)
        {
            LogService.Warn($"[Memory] 경로 범위 규칙 주입 실패: {ex.Message}");
        }
    }
}
