using System.Text.Json;
using AxCopilot.Models;
using AxCopilot.Services;

namespace AxCopilot.Services.Agent;

public partial class AgentLoopService
{
    /// <summary>사용자 요청이 문서/보고서 생성인지 판단합니다.</summary>
    private static bool IsDocumentCreationRequest(string query)
    {
        if (string.IsNullOrWhiteSpace(query)) return false;
        // 문서 생성 관련 키워드 패턴
        var keywords = new[]
        {
            "보고서", "리포트", "report", "문서", "작성해", "써줘", "써 줘", "만들어",
            "분석서", "제안서", "기획서", "회의록", "매뉴얼", "가이드",
            "excel", "엑셀", "docx", "word", "html", "pptx", "ppt",
            "프레젠테이션", "발표자료", "슬라이드"
        };
        var q = query.ToLowerInvariant();
        return keywords.Any(k => q.Contains(k, StringComparison.OrdinalIgnoreCase));
    }

    /// <summary>문서 생성 도구인지 확인합니다 (Cowork 검증 대상).</summary>
    private static bool IsDocumentCreationTool(string toolName)
    {
        return toolName is "file_write" or "docx_create" or "html_create"
            or "excel_create" or "csv_create" or "script_create" or "pptx_create";
    }

    /// <summary>
    /// 이 도구가 성공하면 작업이 완료된 것으로 간주해 루프를 즉시 종료합니다.
    /// Ollama 등 멀티턴 tool_result 미지원 모델에서 불필요한 추가 LLM 호출과 "도구 호출 거부" 오류를 방지합니다.
    /// document_assemble/html_create/docx_create 같은 최종 파일 생성 도구가 해당합니다.
    /// </summary>
    private static bool IsTerminalDocumentTool(string toolName)
    {
        return toolName is "html_create" or "docx_create" or "excel_create"
            or "pptx_create" or "document_assemble" or "csv_create";
    }

    /// <summary>코드 생성/수정 도구인지 확인합니다 (Code 검증 대상).</summary>
    private static bool IsCodeVerificationTarget(string toolName)
    {
        return toolName is "file_write" or "file_edit" or "script_create"
            or "process"; // 빌드/테스트 실행 결과 검증
    }

    /// <summary>
    /// 문서 생성 도구 실행 후 검증 전용 LLM 호출을 삽입합니다.
    /// LLM에게 생성된 파일을 읽고 품질을 평가하도록 강제합니다.
    /// OpenHands 등 오픈소스에서는 이런 강제 검증이 없으며, AX Copilot 차별화 포인트입니다.
    /// </summary>
    /// <summary>읽기 전용 검증 도구 목록 (file_read만 허용)</summary>
    private static readonly HashSet<string> VerificationAllowedTools = ["file_read", "directory_list"];

    private async Task RunPostToolVerificationAsync(
        List<ChatMessage> messages, string toolName, ToolResult result,
        AgentContext context, CancellationToken ct)
    {
        EmitEvent(AgentEventType.Thinking, "", "🔍 생성 결과물 검증 중...");

        // 생성된 파일 경로 추출
        var filePath = result.FilePath ?? "";
        var fileRef = string.IsNullOrEmpty(filePath) ? "방금 생성한 결과물" : $"파일 '{filePath}'";

        // 탭별 검증 프롬프트 생성 — 읽기 + 보고만 (수정 금지)
        var isCodeTab = context.ActiveTab == "Code";
        var checkList = isCodeTab
            ? "   - 구문 오류가 없는가?\n" +
              "   - 참조하는 클래스/메서드/변수가 존재하는가?\n" +
              "   - 코딩 컨벤션이 일관적인가?\n" +
              "   - 에지 케이스 처리가 누락되지 않았는가?"
            : "   - 사용자 요청에 맞는 내용이 모두 포함되었는가?\n" +
              "   - 구조와 형식이 올바른가?\n" +
              "   - 누락된 섹션이나 불완전한 내용이 없는가?\n" +
              "   - 한국어 맞춤법/표현이 자연스러운가?";

        var verificationPrompt = new ChatMessage
        {
            Role = "user",
            Content = $"[System:Verification] {fileRef}을 검증하세요.\n" +
                      "1. file_read 도구로 생성된 파일의 내용을 읽으세요.\n" +
                      "2. 다음 항목을 확인하세요:\n" +
                      checkList + "\n" +
                      "3. 결과를 간단히 보고하세요. 문제가 있으면 구체적으로 무엇이 잘못되었는지 설명하세요.\n" +
                      "⚠️ 중요: 이 단계에서는 파일을 직접 수정하지 마세요. 보고만 하세요."
        };

        // 검증 메시지를 임시로 추가 (검증 완료 후 전부 제거)
        var insertIndex = messages.Count;
        messages.Add(verificationPrompt);
        var addedMessages = new List<ChatMessage> { verificationPrompt };

        try
        {
            // 읽기 전용 도구만 제공 (file_write, file_edit 등 쓰기 도구 차단)
            var allTools = _tools.GetActiveTools(_settings.Settings.Llm.DisabledTools);
            var readOnlyTools = allTools
                .Where(t => VerificationAllowedTools.Contains(t.Name))
                .ToList();

            var verifyBlocks = await _llm.SendWithToolsAsync(messages, readOnlyTools, ct);

            // 검증 응답 처리
            var verifyText = new List<string>();
            var verifyToolCalls = new List<LlmService.ContentBlock>();

            foreach (var block in verifyBlocks)
            {
                if (block.Type == "text" && !string.IsNullOrWhiteSpace(block.Text))
                    verifyText.Add(block.Text);
                else if (block.Type == "tool_use")
                    verifyToolCalls.Add(block);
            }

            var verifyResponse = string.Join("\n", verifyText);

            // file_read 도구 호출 처리 (읽기만 허용)
            if (verifyToolCalls.Count > 0)
            {
                var contentBlocks = new List<object>();
                if (!string.IsNullOrEmpty(verifyResponse))
                    contentBlocks.Add(new { type = "text", text = verifyResponse });
                foreach (var tc in verifyToolCalls)
                    contentBlocks.Add(new { type = "tool_use", id = tc.ToolId, name = tc.ToolName, input = tc.ToolInput });
                var assistantContent = JsonSerializer.Serialize(new { _tool_use_blocks = contentBlocks });
                var assistantMsg = new ChatMessage { Role = "assistant", Content = assistantContent };
                messages.Add(assistantMsg);
                addedMessages.Add(assistantMsg);

                foreach (var tc in verifyToolCalls)
                {
                    var tool = _tools.Get(tc.ToolName);
                    if (tool == null)
                    {
                        var errMsg = LlmService.CreateToolResultMessage(tc.ToolId, tc.ToolName, "검증 단계에서는 읽기 도구만 사용 가능합니다.");
                        messages.Add(errMsg);
                        addedMessages.Add(errMsg);
                        continue;
                    }

                    EmitEvent(AgentEventType.ToolCall, tc.ToolName, $"[검증] {FormatToolCallSummary(tc)}");
                    try
                    {
                        var input = tc.ToolInput ?? System.Text.Json.JsonDocument.Parse("{}").RootElement;
                        var verifyResult = await tool.ExecuteAsync(input, context, ct);
                        var toolMsg = LlmService.CreateToolResultMessage(
                            tc.ToolId, tc.ToolName, TruncateOutput(verifyResult.Output, Defaults.ToolResultTruncateLength));
                        messages.Add(toolMsg);
                        addedMessages.Add(toolMsg);
                    }
                    catch (Exception ex)
                    {
                        var errMsg = LlmService.CreateToolResultMessage(
                            tc.ToolId, tc.ToolName, $"검증 도구 실행 오류: {ex.Message}");
                        messages.Add(errMsg);
                        addedMessages.Add(errMsg);
                    }
                }

                // file_read 결과를 받은 후 최종 검증 판단을 받기 위해 한 번 더 호출
                var finalBlocks = await _llm.SendWithToolsAsync(messages, readOnlyTools, ct);
                verifyResponse = string.Join("\n",
                    finalBlocks.Where(b => b.Type == "text" && !string.IsNullOrWhiteSpace(b.Text)).Select(b => b.Text));
            }

            // 검증 결과를 이벤트로 표시
            if (!string.IsNullOrEmpty(verifyResponse))
            {
                var summary = verifyResponse.Length > Defaults.VerificationSummaryMaxLength ? verifyResponse[..Defaults.VerificationSummaryMaxLength] + "…" : verifyResponse;
                EmitEvent(AgentEventType.Thinking, "", $"✅ 검증 결과: {summary}");

                // 문제가 발견된 경우: 검증 보고서를 컨텍스트에 남겨서 다음 루프에서 자연스럽게 수정
                var hasIssues = verifyResponse.Contains("문제") || verifyResponse.Contains("수정") ||
                                verifyResponse.Contains("누락") || verifyResponse.Contains("오류") ||
                                verifyResponse.Contains("잘못") || verifyResponse.Contains("부족");
                if (hasIssues)
                {
                    // 검증 관련 임시 메시지를 모두 제거
                    foreach (var msg in addedMessages)
                        messages.Remove(msg);

                    // 검증 보고서만 간결하게 남기기 (다음 루프에서 LLM이 자연스럽게 수정)
                    messages.Add(new ChatMessage
                    {
                        Role = "user",
                        Content = $"[System] 방금 생성한 {fileRef}에 대한 자동 검증 결과, 다음 문제가 발견되었습니다:\n{verifyResponse}\n\n위 문제를 수정해 주세요."
                    });
                    return;
                }
            }
        }
        catch (Exception ex)
        {
            EmitEvent(AgentEventType.Error, "", $"검증 LLM 호출 실패: {ex.Message}");
        }

        // 검증 통과 또는 실패: 임시 메시지 전부 제거 (컨텍스트 오염 방지)
        foreach (var msg in addedMessages)
            messages.Remove(msg);
    }

    private void EmitEvent(AgentEventType type, string toolName, string summary,
        string? filePath = null, int stepCurrent = 0, int stepTotal = 0, List<string>? steps = null,
        long elapsedMs = 0, int inputTokens = 0, int outputTokens = 0,
        string? toolInput = null, int iteration = 0)
    {
        // AgentLogLevel에 따라 이벤트 필터링
        var logLevel = _settings.Settings.Llm.AgentLogLevel;

        // simple: ToolCall, ToolResult, Error, Complete, StepStart, StepDone, Decision만
        if (logLevel == "simple" && type is AgentEventType.Thinking or AgentEventType.Planning)
            return;

        // simple: Summary 200자 제한
        if (logLevel == "simple" && summary.Length > 200)
            summary = summary[..200] + "…";

        // debug 아닌 경우 ToolInput 제거
        if (logLevel != "debug")
            toolInput = null;

        var evt = new AgentEvent
        {
            Type = type,
            ToolName = toolName,
            Summary = summary,
            FilePath = filePath,
            Success = type != AgentEventType.Error,
            StepCurrent = stepCurrent,
            StepTotal = stepTotal,
            Steps = steps,
            ElapsedMs = elapsedMs,
            InputTokens = inputTokens,
            OutputTokens = outputTokens,
            ToolInput = toolInput,
            Iteration = iteration,
        };

        if (Dispatcher != null)
            Dispatcher(() => { Events.Add(evt); EventOccurred?.Invoke(evt); });
        else
        {
            Events.Add(evt);
            EventOccurred?.Invoke(evt);
        }
    }

    /// <summary>영향 범위 기반 의사결정 체크. 확인이 필요하면 메시지를 반환, 불필요하면 null.</summary>
    private string? CheckDecisionRequired(LlmService.ContentBlock call, AgentContext context)
    {
        var level = _settings.Settings.Llm.AgentDecisionLevel ?? "normal";
        var toolName = call.ToolName ?? "";
        var input = call.ToolInput;

        // Git 커밋 — 수준에 관계없이 무조건 확인
        if (toolName == "git_tool")
        {
            var action = input?.TryGetProperty("action", out var a) == true ? a.GetString() : "";
            if (action == "commit")
            {
                var msg = input?.TryGetProperty("args", out var m) == true ? m.GetString() : "";
                return $"Git 커밋을 실행하시겠습니까?\n\n커밋 메시지: {msg}";
            }
        }

        // minimal: 파일 삭제, 외부 명령만
        if (level == "minimal")
        {
            // process 도구 (외부 명령 실행)
            if (toolName == "process")
            {
                var cmd = input?.TryGetProperty("command", out var c) == true ? c.GetString() : "";
                return $"외부 명령을 실행하시겠습니까?\n\n명령: {cmd}";
            }
            return null;
        }

        // normal: + 새 파일 생성, 여러 파일 수정, 문서 생성, 외부 명령
        if (level == "normal" || level == "detailed")
        {
            // 외부 명령 실행
            if (toolName == "process")
            {
                var cmd = input?.TryGetProperty("command", out var c) == true ? c.GetString() : "";
                return $"외부 명령을 실행하시겠습니까?\n\n명령: {cmd}";
            }

            // 새 파일 생성
            if (toolName == "file_write")
            {
                var path = input?.TryGetProperty("file_path", out var p) == true ? p.GetString() : "";
                if (!string.IsNullOrEmpty(path))
                {
                    var fullPath = System.IO.Path.IsPathRooted(path) ? path
                        : System.IO.Path.Combine(context.WorkFolder, path ?? "");
                    if (!System.IO.File.Exists(fullPath))
                        return $"새 파일을 생성하시겠습니까?\n\n경로: {path}";
                }
            }

            // 문서 생성 (Excel, Word, HTML 등)
            if (toolName is "excel_create" or "docx_create" or "html_create" or "csv_create" or "script_create")
            {
                var path = input?.TryGetProperty("file_path", out var p) == true ? p.GetString() : "";
                return $"문서를 생성하시겠습니까?\n\n도구: {toolName}\n경로: {path}";
            }

            // 빌드/테스트 실행
            if (toolName is "build_run" or "test_loop")
            {
                var action = input?.TryGetProperty("action", out var a) == true ? a.GetString() : "";
                return $"빌드/테스트를 실행하시겠습니까?\n\n도구: {toolName}\n액션: {action}";
            }
        }

        // detailed: 모든 파일 수정
        if (level == "detailed")
        {
            if (toolName is "file_write" or "file_edit")
            {
                var path = input?.TryGetProperty("file_path", out var p) == true ? p.GetString() : "";
                return $"파일을 수정하시겠습니까?\n\n경로: {path}";
            }
        }

        return null;
    }

    private static string FormatToolCallSummary(LlmService.ContentBlock call)
    {
        if (call.ToolInput == null) return call.ToolName;
        try
        {
            // 주요 파라미터만 표시
            var input = call.ToolInput.Value;
            if (input.TryGetProperty("path", out var path))
                return $"{call.ToolName}: {path.GetString()}";
            if (input.TryGetProperty("command", out var cmd))
                return $"{call.ToolName}: {cmd.GetString()}";
            if (input.TryGetProperty("pattern", out var pat))
                return $"{call.ToolName}: {pat.GetString()}";
            return call.ToolName;
        }
        catch (Exception) { return call.ToolName; }
    }
}
