/**
 * MCP Error Handler
 *
 * DNA-007 에러 추출 시스템 기반 MCP 에러 처리
 */

import {
  extractErrorInfo,
  isRetryableError,
  type ErrorCategory,
  type ExtractedErrorInfo,
} from "@mandujs/core";

/**
 * MCP 에러 응답 타입
 */
export interface McpErrorResponse {
  /** 에러 메시지 */
  error: string;
  /** 에러 코드 (있는 경우) */
  code?: string;
  /** 에러 카테고리 (DNA-007) */
  category: ErrorCategory;
  /** 재시도 가능 여부 */
  retryable: boolean;
  /** 추가 컨텍스트 */
  context?: Record<string, unknown>;
  /** 복구 제안 */
  suggestion?: string;
}

/**
 * MCP 도구 응답 타입
 */
export interface McpToolResponse {
  content: Array<{ type: string; text: string }>;
  isError?: boolean;
  _meta?: McpResponseMeta;
}

export type McpNextActionKind = "none" | "inspect" | "verify" | "repair" | "retry";

export interface McpNextAction {
  kind: McpNextActionKind;
  reason: string;
  command?: string;
  tool?: string;
  input?: unknown;
}

export interface McpResponseMeta {
  toolName: string;
  ok: boolean;
  nextAction: McpNextAction;
}

/**
 * 에러를 MCP 응답 형식으로 변환
 *
 * @example
 * ```ts
 * try {
 *   await tool.execute(args);
 * } catch (err) {
 *   const response = formatMcpError(err, "mandu_guard_check");
 *   // { error: "...", code: "...", category: "validation", ... }
 * }
 * ```
 */
export function formatMcpError(err: unknown, toolName?: string): McpErrorResponse {
  const info = extractErrorInfo(err);

  return {
    error: info.message,
    code: info.code,
    category: info.category,
    retryable: isRetryableError(err),
    context: {
      ...info.context,
      toolName,
      errorName: info.name,
    },
    suggestion: generateSuggestion(info, toolName),
  };
}

/**
 * 에러 카테고리 및 도구별 복구 제안 생성
 */
function generateSuggestion(info: ExtractedErrorInfo, toolName?: string): string | undefined {
  // 도구별 특화 제안
  if (toolName) {
    const toolSuggestion = getToolSpecificSuggestion(toolName, info);
    if (toolSuggestion) return toolSuggestion;
  }

  // 카테고리별 일반 제안
  switch (info.category) {
    case "network":
      return "네트워크 연결을 확인하고 다시 시도해주세요.";

    case "timeout":
      return "요청 시간이 초과되었습니다. 잠시 후 다시 시도해주세요.";

    case "auth":
      return "인증 정보를 확인해주세요.";

    case "validation":
      return "입력 값을 확인해주세요. 필수 파라미터가 누락되었거나 형식이 올바르지 않을 수 있습니다.";

    case "config":
      return "설정 파일(mandu.config.ts)을 확인해주세요.";

    case "system":
      if (info.code === "ENOENT") {
        const path = info.context?.path ?? "unknown";
        return `파일 또는 디렉토리를 찾을 수 없습니다: ${path}`;
      }
      if (info.code === "EACCES" || info.code === "EPERM") {
        return "파일 접근 권한을 확인해주세요.";
      }
      return "시스템 리소스를 확인해주세요.";

    case "external":
      return "외부 서비스에 문제가 있습니다. 잠시 후 다시 시도해주세요.";

    case "internal":
      return "내부 오류가 발생했습니다. 문제가 지속되면 이슈를 보고해주세요.";

    default:
      return undefined;
  }
}

/**
 * 도구별 특화된 에러 제안
 */
function getToolSpecificSuggestion(toolName: string, info: ExtractedErrorInfo): string | undefined {
  // spec 관련 도구
  if (toolName.startsWith("mandu_") && toolName.includes("route")) {
    if (info.code === "ENOENT") {
      return "routes.manifest.json 파일이 없습니다. `mandu init`을 먼저 실행해주세요.";
    }
    if (info.message.includes("not found")) {
      return "해당 라우트를 찾을 수 없습니다. `mandu_list_routes`로 존재하는 라우트를 확인해주세요.";
    }
  }

  // guard 관련 도구
  if (toolName === "mandu_guard_check") {
    if (info.category === "config") {
      return "Guard 설정을 확인해주세요. mandu.config.ts의 guard 섹션을 검토해주세요.";
    }
  }

  // contract 관련 도구
  if (toolName.includes("contract")) {
    if (info.category === "validation") {
      return "Contract 스키마가 올바른지 확인해주세요. Zod 스키마 문법을 확인해주세요.";
    }
  }

  // generate 관련 도구
  if (toolName === "mandu.generate" || toolName === "mandu_generate") {
    if (info.code === "EEXIST") {
      return "파일이 이미 존재합니다. 덮어쓰려면 force 옵션을 사용해주세요.";
    }
  }

  // transaction 관련 도구
  if (toolName.includes("tx") || toolName.includes("transaction")) {
    if (info.message.includes("no active")) {
      return "활성화된 트랜잭션이 없습니다. `mandu_begin`으로 트랜잭션을 시작해주세요.";
    }
  }

  return undefined;
}

/**
 * 도구 실행 결과를 MCP 응답으로 변환
 *
 * @example
 * ```ts
 * // 성공 응답
 * const response = createToolResponse("mandu_list_routes", { routes: [...] });
 *
 * // 에러 응답
 * const response = createToolResponse("mandu_list_routes", null, new Error("..."));
 * ```
 */
/**
 * Detect "soft error" results: handler returned { error: "..." } or { success: false, error: "..." }
 * without throwing. These should still be flagged as isError for the MCP client.
 */
function isSoftErrorResult(result: unknown): boolean {
  if (result == null || typeof result !== "object") return false;
  const obj = result as Record<string, unknown>;
  // Has an explicit "error" field (string or array)
  if ("error" in obj && obj.error != null) return true;
  // Has success: false with no data payload
  if (obj.success === false && "error" in obj) return true;
  return false;
}

function inferNextAction(toolName: string, result: unknown, errorResponse?: McpErrorResponse): McpNextAction {
  if (errorResponse) {
    return {
      kind: errorResponse.retryable ? "retry" : "repair",
      reason: errorResponse.suggestion ?? errorResponse.error,
    };
  }

  if (result && typeof result === "object") {
    const obj = result as Record<string, unknown>;
    if (isSoftErrorResult(result)) {
      return { kind: "repair", reason: String(obj.error ?? "Tool returned an error result.") };
    }
    if (typeof obj.nextVerifyCommand === "string") {
      return { kind: "verify", reason: "Tool returned an explicit verification command.", command: obj.nextVerifyCommand };
    }
    if (typeof obj.nextRepairInput === "string") {
      return {
        kind: "repair",
        reason: "Tool returned a repair input artifact.",
        command: `mandu agent repair --from ${obj.nextRepairInput}`,
        input: obj.nextRepairInput,
      };
    }
    if (obj.dryRun === true) {
      return { kind: "inspect", reason: "Dry-run completed; review the preview before applying changes." };
    }
    if (Array.isArray(obj.nextSteps) && obj.nextSteps.length > 0) {
      return { kind: "inspect", reason: "Tool returned follow-up steps.", input: obj.nextSteps };
    }
    if (typeof obj.tip === "string") {
      return { kind: "inspect", reason: obj.tip };
    }
  }

  return { kind: "none", reason: `${toolName} completed successfully.` };
}

function attachMeta(payload: unknown, meta: McpResponseMeta): unknown {
  if (payload && typeof payload === "object" && !Array.isArray(payload)) {
    return { ...(payload as Record<string, unknown>), _meta: meta };
  }
  return { data: payload, _meta: meta };
}

export function createToolResponse(
  toolName: string,
  result: unknown,
  error?: unknown
): McpToolResponse {
  if (error) {
    const errorResponse = formatMcpError(error, toolName);
    const meta: McpResponseMeta = {
      toolName,
      ok: false,
      nextAction: inferNextAction(toolName, null, errorResponse),
    };
    return {
      content: [
        {
          type: "text",
          text: JSON.stringify(attachMeta(errorResponse, meta), null, 2),
        },
      ],
      isError: true,
      _meta: meta,
    };
  }

  // Detect soft errors returned by handlers (e.g. { error: "Route not found" })
  const softError = isSoftErrorResult(result);
  const meta: McpResponseMeta = {
    toolName,
    ok: !softError,
    nextAction: inferNextAction(toolName, result),
  };

  return {
    content: [
      {
        type: "text",
        text: JSON.stringify(attachMeta(result, meta), null, 2),
      },
    ],
    ...(softError && { isError: true }),
    _meta: meta,
  };
}

/**
 * 에러 응답인지 확인
 */
export function isErrorResponse(response: McpToolResponse): boolean {
  return response.isError === true;
}

/**
 * 에러 응답에서 McpErrorResponse 추출
 */
export function extractErrorFromResponse(response: McpToolResponse): McpErrorResponse | null {
  if (!response.isError) return null;

  try {
    const text = response.content[0]?.text;
    if (!text) return null;
    return JSON.parse(text) as McpErrorResponse;
  } catch {
    return null;
  }
}

/**
 * 에러 로깅 헬퍼
 */
export function logToolError(
  toolName: string,
  error: unknown,
  args?: Record<string, unknown>
): void {
  const info = extractErrorInfo(error);

  console.error(`[MCP:${toolName}] ${info.category.toUpperCase()}: ${info.message}`);

  if (info.code) {
    console.error(`  Code: ${info.code}`);
  }

  if (args && Object.keys(args).length > 0) {
    console.error(`  Args:`, JSON.stringify(args, null, 2));
  }

  if (info.context && Object.keys(info.context).length > 0) {
    console.error(`  Context:`, info.context);
  }
}
