using System.IO;
using System.Text.Json;
using System.Text.Json.Serialization;
using AxCopilot.Models;

namespace AxCopilot.Services.Agent;

/// <summary>
/// 에이전트 도구의 공통 인터페이스.
/// LLM function calling을 통해 호출되며, JSON 파라미터를 받아 결과를 반환합니다.
/// </summary>
public interface IAgentTool
{
    /// <summary>LLM에 노출되는 도구 이름 (snake_case). 예: "file_read"</summary>
    string Name { get; }

    /// <summary>LLM에 전달되는 도구 설명.</summary>
    string Description { get; }

    /// <summary>LLM function calling용 파라미터 JSON Schema.</summary>
    ToolParameterSchema Parameters { get; }

    /// <summary>도구를 실행하고 결과를 반환합니다.</summary>
    /// <param name="args">LLM이 생성한 JSON 파라미터</param>
    /// <param name="context">실행 컨텍스트 (작업 폴더, 권한 등)</param>
    /// <param name="ct">취소 토큰</param>
    Task<ToolResult> ExecuteAsync(JsonElement args, AgentContext context, CancellationToken ct = default);
}

/// <summary>도구 실행 결과.</summary>
public class ToolResult
{
    /// <summary>성공 여부.</summary>
    public bool Success { get; init; }

    /// <summary>결과 텍스트 (LLM에 피드백).</summary>
    public string Output { get; init; } = "";

    /// <summary>생성/수정된 파일 경로 (UI 표시용).</summary>
    public string? FilePath { get; init; }

    /// <summary>오류 메시지 (실패 시).</summary>
    public string? Error { get; init; }

    public static ToolResult Ok(string output, string? filePath = null) =>
        new() { Success = true, Output = output, FilePath = filePath };

    public static ToolResult Fail(string error) =>
        new() { Success = false, Output = error, Error = error };
}

/// <summary>도구 파라미터 JSON Schema (LLM function calling용).</summary>
public class ToolParameterSchema
{
    [JsonPropertyName("type")]
    public string Type { get; init; } = "object";

    [JsonPropertyName("properties")]
    public Dictionary<string, ToolProperty> Properties { get; init; } = new();

    [JsonPropertyName("required")]
    public List<string> Required { get; init; } = new();
}

/// <summary>파라미터 속성 정의.</summary>
public class ToolProperty
{
    [JsonPropertyName("type")]
    public string Type { get; init; } = "string";

    [JsonPropertyName("description")]
    public string Description { get; init; } = "";

    [JsonPropertyName("enum")]
    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
    public List<string>? Enum { get; init; }

    /// <summary>array 타입일 때 항목 스키마. Gemini API 필수.</summary>
    [JsonPropertyName("items")]
    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
    public ToolProperty? Items { get; init; }
}

/// <summary>에이전트 실행 컨텍스트.</summary>
public class AgentContext
{
    /// <summary>앱 설정 참조 — <c>Application.Current as App</c> 서비스 로케이터 패턴 대체.</summary>
    public AppSettings? Settings { get; init; }

    /// <summary>LLM 설정 바로가기.</summary>
    public LlmSettings? Llm => Settings?.Llm;

    /// <summary>작업 폴더 경로.</summary>
    public string WorkFolder { get; init; } = "";

    /// <summary>파일 접근 권한. Ask | Auto | Deny</summary>
    public string Permission { get; init; } = "Ask";

    /// <summary>도구별 권한 오버라이드. 키: 도구명, 값: "ask" | "auto" | "deny".</summary>
    public Dictionary<string, string> ToolPermissions { get; init; } = new();

    /// <summary>차단 경로 패턴 목록.</summary>
    public List<string> BlockedPaths { get; init; } = new();

    /// <summary>차단 확장자 목록.</summary>
    public List<string> BlockedExtensions { get; init; } = new();

    /// <summary>현재 활성 탭. "Chat" | "Cowork" | "Code".</summary>
    public string ActiveTab { get; init; } = "Chat";

    /// <summary>개발자 모드: 상세 이력 표시.</summary>
    public bool DevMode { get; init; }

    /// <summary>개발자 모드: 도구 실행 전 매번 사용자 승인 대기.</summary>
    public bool DevModeStepApproval { get; init; }

    /// <summary>권한 확인 콜백 (Ask 모드). 반환값: true=승인, false=거부.</summary>
    public Func<string, string, Task<bool>>? AskPermission { get; init; }

    /// <summary>사용자 의사결정 콜백. (질문, 선택지) → 사용자 응답 문자열.</summary>
    public Func<string, List<string>, Task<string?>>? UserDecision { get; init; }

    /// <summary>에이전트 질문 콜백 (UserAskTool 전용). (질문, 선택지, 기본값) → 사용자 응답.</summary>
    public Func<string, List<string>, string, Task<string?>>? UserAskCallback { get; init; }

    /// <summary>경로가 허용되는지 확인합니다.</summary>
    public bool IsPathAllowed(string path)
    {
        var fullPath = Path.GetFullPath(path);

        // 차단 확장자 검사
        var ext = Path.GetExtension(fullPath).ToLowerInvariant();
        if (BlockedExtensions.Any(e => string.Equals(e, ext, StringComparison.OrdinalIgnoreCase)))
            return false;

        // 차단 경로 패턴 검사
        foreach (var pattern in BlockedPaths)
        {
            // 간단한 와일드카드 매칭: *\Windows\* → fullPath에 \Windows\ 포함 시 차단
            var clean = pattern.Replace("*", "");
            if (!string.IsNullOrEmpty(clean) && fullPath.Contains(clean, StringComparison.OrdinalIgnoreCase))
                return false;
        }

        // 작업 폴더 제한: 작업 폴더가 설정되어 있으면 하위 경로만 허용
        if (!string.IsNullOrEmpty(WorkFolder))
        {
            var workFull = Path.GetFullPath(WorkFolder);
            if (!fullPath.StartsWith(workFull, StringComparison.OrdinalIgnoreCase))
                return false;
        }

        return true;
    }

    /// <summary>
    /// 파일 경로에 타임스탬프를 추가합니다.
    /// 예: report.html → report_20260328_1430.html
    /// 동일 이름 파일이 이미 존재하면 자동으로 타임스탬프를 붙입니다.
    /// </summary>
    public static string EnsureTimestampedPath(string fullPath)
    {
        var dir = Path.GetDirectoryName(fullPath) ?? "";
        var name = Path.GetFileNameWithoutExtension(fullPath);
        var ext = Path.GetExtension(fullPath);
        var stamp = DateTime.Now.ToString("yyyyMMdd_HHmm");

        // 이미 타임스탬프가 포함된 파일명이면 그대로 사용
        if (System.Text.RegularExpressions.Regex.IsMatch(name, @"_\d{8}_\d{4}$"))
            return fullPath;

        var timestamped = Path.Combine(dir, $"{name}_{stamp}{ext}");
        return timestamped;
    }

    /// <summary>파일 쓰기/수정 권한을 확인합니다. 도구별 권한 오버라이드를 우선 적용합니다.</summary>
    public async Task<bool> CheckWritePermissionAsync(string toolName, string filePath)
    {
        // 도구별 권한 오버라이드 확인
        var effectivePerm = Permission;
        if (ToolPermissions.TryGetValue(toolName, out var toolPerm))
            effectivePerm = toolPerm;

        if (string.Equals(effectivePerm, "Deny", StringComparison.OrdinalIgnoreCase)) return false;
        if (string.Equals(effectivePerm, "Auto", StringComparison.OrdinalIgnoreCase)) return true;
        // Ask 모드
        if (AskPermission != null)
            return await AskPermission(toolName, filePath);
        return false;
    }
}

/// <summary>에이전트 이벤트 (UI 표시용).</summary>
public class AgentEvent
{
    public DateTime Timestamp { get; init; } = DateTime.Now;
    public AgentEventType Type { get; init; }
    public string ToolName { get; init; } = "";
    public string Summary { get; init; } = "";
    public string? FilePath { get; init; }
    public bool Success { get; init; } = true;

    /// <summary>Task Decomposition: 현재 단계 / 전체 단계 (진행률 표시용).</summary>
    public int StepCurrent { get; init; }
    public int StepTotal { get; init; }
    /// <summary>Task Decomposition: 단계 목록.</summary>
    public List<string>? Steps { get; init; }

    // ── 워크플로우 분석기용 확장 필드 ──

    /// <summary>도구 실행 소요 시간 (ms). 0이면 미측정.</summary>
    public long ElapsedMs { get; init; }

    /// <summary>이번 LLM 호출의 입력 토큰 수.</summary>
    public int InputTokens { get; init; }

    /// <summary>이번 LLM 호출의 출력 토큰 수.</summary>
    public int OutputTokens { get; init; }

    /// <summary>도구 파라미터 JSON (debug 모드에서만 기록).</summary>
    public string? ToolInput { get; init; }

    /// <summary>현재 에이전트 루프 반복 번호.</summary>
    public int Iteration { get; init; }
}

public enum AgentEventType
{
    Thinking,    // LLM 사고 중
    Planning,    // 작업 계획 수립
    StepStart,   // 단계 시작
    StepDone,    // 단계 완료
    ToolCall,    // 도구 호출
    ToolResult,  // 도구 결과
    SkillCall,   // 스킬 호출
    Error,       // 오류
    Complete,    // 완료
    Decision,    // 사용자 의사결정 대기
    Paused,      // 에이전트 일시정지
    Resumed,     // 에이전트 재개
}
