/**
 * /database/api/* 路由共享件:依赖装配、actor 门禁、统一错误映射。
 * 约定(与 admin 面一致):绝对路径;guard 前置 fail closed;查询 scope 到 silo org。
 */

import type { FastifyReply, FastifyRequest } from "fastify";
import { Prisma } from "@prisma/client";
import type { PrismaClient } from "@prisma/client";
import { FileLibError } from "./model.js";
import { requireFileLibActor } from "./guards.js";
import type { FileLibActor, TreeServiceDeps } from "./treeService.js";
import type { GroupResolver } from "./groupResolver.js";
import type { VersionStore } from "./versionStore.js";
import type { ExportAdapter } from "./exportService.js";

export interface FileLibRouteDeps {
  readonly prisma: PrismaClient;
  readonly sessionSecret: string;
  readonly organizationId: string;
  readonly storageRoot: string;
  readonly groupResolver: GroupResolver;
  readonly versionStore: VersionStore;
  readonly exportAdapters: readonly ExportAdapter[];
}

/** 组装 treeService 依赖(路由处理内直接使用)。 */
export function treeDeps(deps: FileLibRouteDeps): TreeServiceDeps {
  return {
    prisma: deps.prisma,
    groupResolver: deps.groupResolver,
    versionStore: deps.versionStore,
    organizationId: deps.organizationId,
    storageRoot: deps.storageRoot,
  };
}

/** 端点第一行调用;null = 响应已发(401/403),fail closed。 */
export async function actorOrNull(
  request: FastifyRequest,
  reply: FastifyReply,
  deps: FileLibRouteDeps,
): Promise<FileLibActor | null> {
  return requireFileLibActor(request, reply, {
    prisma: deps.prisma,
    sessionSecret: deps.sessionSecret,
    organizationId: deps.organizationId,
  });
}

/** 统一错误出口:FileLibError → 语义码;P2002 → 409;其余 → 500(不泄露内部)。 */
export async function sendRouteError(reply: FastifyReply, error: unknown): Promise<void> {
  if (error instanceof FileLibError) {
    await reply.status(error.statusCode).send({
      error: { code: error.code, message: error.message, ...(error.details ?? {}) },
    });
    return;
  }
  if (error instanceof Prisma.PrismaClientKnownRequestError && error.code === "P2002") {
    await reply.status(409).send({ error: { code: "conflict", message: "uniqueness conflict" } });
    return;
  }
  reply.log.error({ err: error }, "filelib route: unexpected error");
  await reply.status(500).send({ error: { code: "internal", message: "internal error" } });
}

/** 请求体轻量校验(抛 FileLibError 400)。 */
export function bodyObject(body: unknown): Record<string, unknown> {
  if (typeof body !== "object" || body === null || Array.isArray(body)) {
    throw new FileLibError(400, "invalid_request", "request body must be a JSON object");
  }
  return body as Record<string, unknown>;
}

export function requireString(obj: Record<string, unknown>, key: string): string {
  const value = obj[key];
  if (typeof value !== "string" || value === "") {
    throw new FileLibError(400, "invalid_request", `missing or invalid field: ${key}`);
  }
  return value;
}

export function optionalString(obj: Record<string, unknown>, key: string): string | undefined {
  const value = obj[key];
  if (value === undefined || value === null) return undefined;
  if (typeof value !== "string") {
    throw new FileLibError(400, "invalid_request", `invalid field: ${key}`);
  }
  return value;
}
