using System.Text;
using System.Text.Json;

namespace AxCopilot.Services.Agent;

/// <summary>
/// 코드베이스 시맨틱 검색 도구.
/// 프로젝트 파일을 TF-IDF로 인덱싱하고, 자연어 질문으로 관련 코드를 찾습니다.
/// SQLite에 인덱스를 영속 저장하여 증분 업데이트와 빠른 재시작을 지원합니다.
/// </summary>
public class CodeSearchTool : IAgentTool
{
    public string Name => "search_codebase";

    public string Description =>
        "코드베이스를 시맨틱 검색합니다. 자연어 질문으로 관련 있는 코드를 찾습니다.\n" +
        "예: '사용자 인증 로직', '데이터베이스 연결 설정', '에러 핸들링 패턴'\n" +
        "인덱스는 영속 저장되어 변경된 파일만 증분 업데이트합니다.";

    public ToolParameterSchema Parameters => new()
    {
        Properties = new()
        {
            ["query"] = new ToolProperty
            {
                Type = "string",
                Description = "검색할 내용 (자연어 또는 키워드)"
            },
            ["max_results"] = new ToolProperty
            {
                Type = "integer",
                Description = "최대 결과 수 (기본 5)"
            },
            ["reindex"] = new ToolProperty
            {
                Type = "boolean",
                Description = "true면 기존 인덱스를 버리고 전체 재인덱싱 (기본 false)"
            },
        },
        Required = new() { "query" }
    };

    private static CodeIndexService? _indexService;
    private static string _lastWorkFolder = "";

    public async Task<ToolResult> ExecuteAsync(JsonElement args, AgentContext context, CancellationToken ct = default)
    {
        // 설정 체크
        if (!(context.Llm?.Code?.EnableCodeIndex ?? true))
            return ToolResult.Ok("코드 시맨틱 검색이 비활성 상태입니다. 설정 → AX Agent → 코드에서 활성화하세요.");

        var query = args.TryGetProperty("query", out var q) ? q.GetString() ?? "" : "";
        var maxResults = args.TryGetProperty("max_results", out var m) ? m.GetInt32() : 5;
        var reindex = args.TryGetProperty("reindex", out var ri) && ri.GetBoolean();

        if (string.IsNullOrWhiteSpace(query))
            return ToolResult.Fail("query가 필요합니다.");

        if (string.IsNullOrEmpty(context.WorkFolder))
            return ToolResult.Fail("작업 폴더가 설정되어 있지 않습니다.");

        // 작업 폴더가 바뀌면 인덱스 서비스 재생성
        if (_indexService == null || _lastWorkFolder != context.WorkFolder || reindex)
        {
            _indexService?.Dispose();
            _indexService = new CodeIndexService();
            _lastWorkFolder = context.WorkFolder;

            // 기존 sqlite 인덱스 로드 시도 (앱 재시작 시 즉시 사용 가능)
            if (!reindex)
                _indexService.TryLoadExisting(context.WorkFolder);
        }

        // 증분 인덱싱 (신규/변경 파일만 처리)
        if (!_indexService.IsIndexed || reindex)
        {
            await _indexService.IndexAsync(context.WorkFolder, ct);
            if (!_indexService.IsIndexed)
                return ToolResult.Fail("코드 인덱싱에 실패했습니다.");
        }

        var results = _indexService.Search(query, maxResults);
        if (results.Count == 0)
            return ToolResult.Ok($"'{query}'에 대한 관련 코드를 찾지 못했습니다. 다른 키워드로 검색해보세요.");

        var sb = new StringBuilder();
        sb.AppendLine($"'{query}' 검색 결과 ({results.Count}개, 인덱스 {_indexService.ChunkCount}개 청크):\n");
        foreach (var r in results)
        {
            sb.AppendLine($"📁 {r.FilePath} (line {r.StartLine}-{r.EndLine}, score {r.Score:F3})");
            sb.AppendLine("```");
            sb.AppendLine(r.Preview);
            sb.AppendLine("```");
            sb.AppendLine();
        }

        return ToolResult.Ok(sb.ToString());
    }

    /// <summary>인덱스를 강제 재빌드합니다.</summary>
    public static void InvalidateIndex()
    {
        _indexService?.Dispose();
        _indexService = null;
        _lastWorkFolder = "";
    }
}
