/**
 * Error handling utilities
 */
import { LoggerFactory } from "./logger.js";
import { config } from "@/config/Config.js";

const logger = LoggerFactory.server().child({ component: "ErrorUtils" });

// Environment flag to control legacy console logging noise. Default enabled to preserve
// backward compatibility and existing test expectations. Set LEGACY_ERROR_LOGS=0 to disable
// the direct console.error side-channel (structured logger still emits).
const LEGACY_ERROR_LOGS_ENABLED = config().error.legacyLogsEnabled;

// Internal helper to avoid sprinkling conditionals
function legacyConsoleError(...args: unknown[]) {
  if (LEGACY_ERROR_LOGS_ENABLED) {
    console.error(...args);
  }
}

// Test hook: exported only for instrumentation in unit tests (tree-shakeable)
// @__PURE__ This constant has no side effects and can be dropped in production builds
export const __errorUtilsLogger = logger;

export function getErrorMessage(error: unknown): string {
  if (error instanceof Error) {
    return error.message;
  }

  if (typeof error === "string") {
    return error;
  }

  if (error && typeof error === "object" && "message" in error) {
    return String(error.message);
  }

  return "Unknown error occurred";
}

export function isError(error: unknown): error is Error {
  return error instanceof Error;
}

export function logAndReturn<T>(error: unknown, defaultValue: T): T {
  const message = getErrorMessage(error);
  // Legacy console logging (can be disabled via LEGACY_ERROR_LOGS=0)
  legacyConsoleError("Error occurred:", message);
  logger.warn("Error occurred - returning default value", {
    error: message,
  });
  return defaultValue;
}

/**
 * Enhanced error handler for consistent tool error handling
 */
export function handleToolError(error: unknown, operation: string, context?: Record<string, unknown>): never {
  const message = getErrorMessage(error);
  const errObj = error instanceof Error ? error : new Error(message);
  // Legacy console logging (can be disabled via LEGACY_ERROR_LOGS=0)
  legacyConsoleError(`Error in ${operation}:`, errObj);
  if (context) {
    legacyConsoleError("Context:", context);
  }
  logger.error(`Error in ${operation}`, {
    error: message,
    ...(context && { context }),
  });

  if (error instanceof Error && error.stack) {
    logger.debug("Error stack trace", { stack: error.stack });
  }

  // Provide more specific error messages based on error content
  if (message.includes("ECONNREFUSED") || message.includes("ENOTFOUND")) {
    throw new Error(
      `Connection failed during ${operation}. Please check your WordPress site URL and network connection.`,
    );
  }

  if (message.includes("401") || message.includes("Unauthorized")) {
    throw new Error(`Authentication failed during ${operation}. Please check your WordPress credentials.`);
  }

  if (message.includes("403") || message.includes("Forbidden")) {
    throw new Error(`Permission denied during ${operation}. Please check your user permissions.`);
  }

  if (message.includes("429") || message.includes("Too Many Requests")) {
    throw new Error(`Rate limit exceeded during ${operation}. Please try again later.`);
  }

  throw new Error(`Failed to ${operation}: ${message}`);
}

/**
 * Validates required parameters
 */
export function validateRequired(params: Record<string, unknown> | unknown, required: string[]): void {
  // Runtime guard: ensure params is a non-null object (tests expect throw on invalid input)
  if (params === null || typeof params !== "object") {
    throw new Error("Parameters must be an object");
  }
  // Only treat undefined or null as missing; accept legitimate falsy values: 0, false, ""
  const obj = params as Record<string, unknown>;
  const missing = required.filter((key) => obj[key] === undefined || obj[key] === null);
  if (missing.length > 0) {
    throw new Error(`Missing required parameters: ${missing.join(", ")}`);
  }
}

/**
 * Validates site parameter for multi-site configurations
 */
export function validateSite(site: string | undefined, availableSites: string[]): string {
  if (!site) {
    if (availableSites.length === 1) {
      return availableSites[0];
    }
    throw new Error(
      `Site parameter is required when multiple sites are configured. Available sites: ${availableSites.join(", ")}`,
    );
  }

  if (!availableSites.includes(site)) {
    throw new Error(`Site '${site}' not found. Available sites: ${availableSites.join(", ")}`);
  }

  return site;
}
