/**
 * 仓库和持久化操作共享的 Result<T>。
 *
 * `error` 固定为 `Error`（非 `unknown`）：`Result.wrap` 捕获后用
 * `ExceptionHandler` 统一归一化为 `AppException`，调用方可以稳定读取
 * `error.message`；需要结构化错误时可继续通过 `ExceptionHandler.handle(error)`
 * 得到简化后的 `{ code, message }`。
 */
import { AppException, ExceptionHandler } from "@/core/errors";

import { Logger } from "./logger";

export type Result<T> =
  | { readonly success: true; readonly data: T }
  | { readonly success: false; readonly error: Error };

/** Result.wrap 专用 logger —— 自动带 component: "Result" 标签 */
const log = new Logger("Result");

type ResultErrorLogLevel = "info" | "error";

export interface ResultWrapOptions {
  errorLogLevel?:
    | ResultErrorLogLevel
    | ((error: Error) => ResultErrorLogLevel);
}

export const Result = {
  /** 构造成功结果。 */
  ok<T>(data: T): Result<T> {
    return { success: true, data };
  },

  /**
   * 构造失败结果。`error` 接受任意 thrown 值，内部用 `toError` 归一化为 `AppException`。
   */
  err<T = never>(error: unknown): Result<T> {
    return { success: false, error: toError(error) };
  },

  /** 类型守卫：true 分支。 */
  isOk<T>(r: Result<T>): r is { readonly success: true; readonly data: T } {
    return r.success;
  },

  /** 类型守卫：false 分支。 */
  isErr<T>(r: Result<T>): r is { readonly success: false; readonly error: Error } {
    return !r.success;
  },

  /** 不可变的链式 map：失败时原样传递。 */
  map<T, U>(r: Result<T>, fn: (v: T) => U): Result<U> {
    return r.success ? Result.ok(fn(r.data)) : r;
  },

  /**
   * 捕获异步 thunk 的异常并包装成 `Result<T>`。仓库内主要用这个把
   * `*Api` 调用从「throw 风格」桥接到「Result 风格」。
   *
   * 错误处理策略（重点）：
   *   1. 捕获后用 `toError` 规范化为 `AppException`，原始 thrown 值放入 `cause`
   *   2. 用 Logger 记录（带 stack + component=Result 标签）—— 调试 dev 控制台可见，
   *      生产环境走 JSON pipeline 接入日志平台
   *   3. 不重新抛出 —— 调用方拿到的永远是 `Result<T>`（throw 风格调用方可在
   *      最外层显式 `throw r.error`）
   */
  async wrap<T>(
    thunk: () => Promise<T>,
    options?: ResultWrapOptions,
  ): Promise<Result<T>> {
    try {
      return Result.ok(await thunk());
    } catch (e) {
      const err = toError(e);
      const logLevel =
        typeof options?.errorLogLevel === "function"
          ? options.errorLogLevel(err)
          : options?.errorLogLevel ?? "error";
      const details = { err, stack: err.stack, type: err.constructor.name };
      if (logLevel === "info") {
        log.info(details, "Result.wrap caught exception");
      } else {
        log.error(details, "Result.wrap caught exception");
      }
      return Result.err(err);
    }
  },
} as const;

/**
 * 把任意 thrown 值规范化为 `AppException`：
 *   - 已是 `AppException` → 原样返回
 *   - 其他错误 → 交给 `ExceptionHandler` 收敛为 `{ code, message }`
 *   - 原始值会保存在 `cause`，方便日志和调试继续追溯
 */
export function toError(value: unknown): AppException {
  return ExceptionHandler.toError(value);
}
