import {
  UploadError,
  type UploadSession,
  type UploadedPart,
} from "./uploads.ts";

interface UploadCoordinator {
  create(input: {
    fileName: string;
    mimeType: string;
    totalBytes: number;
  }): Promise<UploadSession>;
  status(id: string): Promise<UploadSession>;
  uploadPart(
    id: string,
    partNumber: number,
    value: ArrayBuffer,
  ): Promise<UploadedPart>;
  complete(id: string): Promise<UploadSession>;
  abort(id: string): Promise<UploadSession>;
}

function statusForError(code: string): number {
  if (code === "upload_not_found") return 404;
  if (code === "upload_expired") return 410;
  if (code === "file_too_large") return 413;
  if (code === "signature_mismatch" || code === "unsupported_mime") return 415;
  if (code === "missing_parts" || code === "upload_closed") return 409;
  return 400;
}

function errorResponse(error: unknown): Response {
  if (error instanceof UploadError) {
    return Response.json(
      { error: { code: error.code, message: error.message } },
      { status: statusForError(error.code) },
    );
  }
  return Response.json(
    {
      error: {
        code: "internal_error",
        message: "服务器暂时无法处理该请求，请稍后重试。",
      },
    },
    { status: 500 },
  );
}

function publicSession(session: UploadSession) {
  return {
    id: session.id,
    fileName: session.fileName,
    mimeType: session.mimeType,
    format: session.format,
    totalBytes: session.totalBytes,
    partSize: session.partSize,
    partCount: session.partCount,
    uploadedParts: session.uploadedParts,
    status: session.status,
    expiresAt: session.expiresAt,
    createdAt: session.createdAt,
    updatedAt: session.updatedAt,
  };
}

async function readJsonObject(request: Request): Promise<Record<string, unknown>> {
  if (!request.headers.get("content-type")?.toLowerCase().startsWith("application/json")) {
    throw new UploadError("invalid_metadata", "请求必须使用 JSON。");
  }
  let value: unknown;
  try {
    value = await request.json();
  } catch {
    throw new UploadError("invalid_metadata", "JSON 内容无法解析。");
  }
  if (!value || typeof value !== "object" || Array.isArray(value)) {
    throw new UploadError("invalid_metadata", "请求内容必须是对象。");
  }
  return value as Record<string, unknown>;
}

export function createUploadApi(dependencies: {
  coordinator: UploadCoordinator;
  registerCompletedBook(session: UploadSession): Promise<unknown>;
}) {
  return {
    async create(request: Request): Promise<Response> {
      try {
        const payload = await readJsonObject(request);
        if (
          typeof payload.fileName !== "string" ||
          typeof payload.mimeType !== "string" ||
          typeof payload.totalBytes !== "number"
        ) {
          throw new UploadError(
            "invalid_metadata",
            "需要 fileName、mimeType 和 totalBytes。",
          );
        }
        const session = await dependencies.coordinator.create({
          fileName: payload.fileName,
          mimeType: payload.mimeType,
          totalBytes: payload.totalBytes,
        });
        return Response.json({ upload: publicSession(session) }, { status: 201 });
      } catch (error) {
        return errorResponse(error);
      }
    },

    async status(id: string): Promise<Response> {
      try {
        const session = await dependencies.coordinator.status(id);
        return Response.json({ upload: publicSession(session) });
      } catch (error) {
        return errorResponse(error);
      }
    },

    async uploadPart(
      request: Request,
      id: string,
      rawPartNumber: string,
    ): Promise<Response> {
      try {
        const contentType = request.headers.get("content-type")?.toLowerCase() ?? "";
        if (contentType !== "application/octet-stream") {
          return Response.json(
            {
              error: {
                code: "unsupported_media_type",
                message: "分片必须使用 application/octet-stream。",
              },
            },
            { status: 415 },
          );
        }
        const partNumber = Number(rawPartNumber);
        if (!Number.isInteger(partNumber) || partNumber < 1) {
          throw new UploadError("invalid_part_number", "分片序号必须是正整数。");
        }
        const part = await dependencies.coordinator.uploadPart(
          id,
          partNumber,
          await request.arrayBuffer(),
        );
        return Response.json({ part });
      } catch (error) {
        return errorResponse(error);
      }
    },

    async complete(id: string): Promise<Response> {
      try {
        const session = await dependencies.coordinator.complete(id);
        const book = await dependencies.registerCompletedBook(session);
        return Response.json({ book }, { status: 201 });
      } catch (error) {
        return errorResponse(error);
      }
    },

    async abort(id: string): Promise<Response> {
      try {
        const session = await dependencies.coordinator.abort(id);
        return Response.json({ upload: publicSession(session) });
      } catch (error) {
        return errorResponse(error);
      }
    },
  };
}
