/**
 * 共享错误处理工具:统一 Server Action 的错误响应与客户端 Action 调用模式。
 *
 * 设计目标:
 * 1. 避免将内部错误消息(如 SQL 错误、堆栈信息)直接暴露给客户端
 * 2. 统一 ActionState<T> 的失败结构
 * 3. 为客户端调用 Server Action 提供 try/catch/finally 包装,防止 UI 永久卡 loading
 */

import type { ActionState } from "@/shared/types/action-state"
import { PermissionDeniedError } from "@/shared/lib/errors"

/**
 * 已知的业务错误类型,消息可以安全返回给客户端。
 * 其他 Error 一律视为系统错误,返回通用消息。
 */
export class BusinessError extends Error {
  constructor(
    message: string,
    public readonly code?: string
  ) {
    super(message)
    this.name = "BusinessError"
  }
}

/**
 * 资源不存在错误。消息可安全返回客户端。
 */
export class NotFoundError extends BusinessError {
  constructor(resource: string) {
    super(`${resource} 不存在`, "not_found")
    this.name = "NotFoundError"
  }
}

/**
 * 输入校验错误。消息可安全返回客户端。
 *
 * `code` 可选，默认 `"validation_error"`。当需要前端按字段精确本地化时
 * 可传入自定义错误码（如 `invalid_date:publishDate`），前端通过 errorCode 查 i18n。
 */
export class ValidationError extends BusinessError {
  constructor(message: string, code?: string) {
    super(message, code ?? "validation_error")
    this.name = "ValidationError"
  }
}

/**
 * 统一的 Server Action 错误处理器。
 *
 * - PermissionDeniedError:返回权限不足消息(可安全暴露)
 * - BusinessError / NotFoundError / ValidationError:返回其 message(可安全暴露)
 * - 其他 Error:返回通用消息,原始错误通过 console.error 记录到服务端日志
 *
 * @returns ActionState<never> 的失败分支
 */
export function handleActionError(e: unknown): ActionState<never> {
  // 权限错误:消息已由 PermissionDeniedError 构造为用户友好文案
  if (e instanceof PermissionDeniedError) {
    return { success: false, message: e.message }
  }

  // 业务错误:消息可安全暴露；保留 code → errorCode 供前端 i18n 查找
  if (e instanceof BusinessError) {
    return { success: false, message: e.message, errorCode: e.code }
  }

  // 未知错误:不暴露内部细节,仅记录服务端日志
  if (e instanceof Error) {
    console.error("[ActionError]", e.name, e.message, e.stack)
    return { success: false, message: "操作失败,请稍后重试", errorCode: "unexpected" }
  }

  console.error("[ActionError] Unknown error:", e)
  return { success: false, message: "操作失败,请稍后重试", errorCode: "unexpected" }
}

/**
 * 安全地调用 Server Action,自动处理 try/catch/finally。
 *
 * 用于客户端组件中调用 Server Action,确保:
 * 1. 网络错误或 Action 抛出异常时,catch 块执行 onError 回调
 * 2. 无论成功失败,finally 块执行 onFinally 回调(用于重置 loading 状态)
 *
 * @example
 * ```tsx
 * const [isSubmitting, setIsSubmitting] = useState(false)
 * const result = await safeActionCall(
 *   () => createGradeRecordAction(null, formData),
 *   {
 *     onError: () => toast.error("保存失败"),
 *     onFinally: () => setIsSubmitting(false),
 *   }
 * )
 * if (result?.success) { toast.success("保存成功") }
 * ```
 */
export async function safeActionCall<T>(
  action: () => Promise<ActionState<T>>,
  options?: {
    onError?: (error: unknown) => void
    onFinally?: () => void
  }
): Promise<ActionState<T> | null> {
  try {
    return await action()
  } catch (e) {
    // Action 抛出异常(非返回 failure),如网络错误、序列化错误等
    options?.onError?.(e)
    console.error("[SafeActionCall]", e)
    return null
  } finally {
    options?.onFinally?.()
  }
}

/**
 * 安全解析 JSON 字符串,失败时抛出 ValidationError。
 *
 * 用于 Server Action 中包装 JSON.parse,避免 SyntaxError 被外层 catch
 * 捕获后暴露解析细节给客户端。
 *
 * @example
 * ```ts
 * const records = safeJsonParse(recordsJson, "成绩数据格式无效")
 * ```
 */
export function safeJsonParse<T>(json: string, errorMessage: string): T {
  try {
    return JSON.parse(json) as T
  } catch {
    throw new ValidationError(errorMessage)
  }
}

/**
 * 校验日期字符串是否有效,无效则抛出 ValidationError。
 *
 * `fieldName` 用于构造错误码 `invalid_date:{fieldName}`，前端通过 errorCode 查 i18n。
 * message 保留中文兜底，前端应优先使用 errorCode 本地化。
 *
 * @returns 解析后的 Date 对象
 */
export function safeParseDate(value: string, fieldName: string): Date {
  const d = new Date(value)
  if (Number.isNaN(d.getTime())) {
    throw new ValidationError(`${fieldName} 格式无效`)
  }
  return d
}

/**
 * 校验数字字符串,无效则抛出 ValidationError。
 *
 * @returns 解析后的 number
 */
export function safeParseNumber(value: string, fieldName: string): number {
  const n = Number(value)
  if (!Number.isFinite(n)) {
    throw new ValidationError(`${fieldName} 必须是有效数字`)
  }
  return n
}

/**
 * 转义 SQL LIKE 通配符(% 和 _),防止用户输入干扰模糊查询。
 *
 * @example
 * ```ts
 * const needle = `%${escapeLikePattern(q)}%`
 * ```
 */
export function escapeLikePattern(input: string): string {
  return input.replace(/[%_\\]/g, "\\$&")
}
