import { HttpError } from "../../shared/api/errors";
import { httpClient, type RequestOptions } from "../../shared/api/httpClient";
import type { JsonResult } from "../../shared/api/types";
import {
  isBindAttachmentsRequest,
  isBindAttachmentsResult,
  isStringId,
  isUnbindAttachmentResult,
  type BindAttachmentsRequest,
  type BindAttachmentsResult,
  type UnbindAttachmentResult,
} from "./bindingTypes";

interface BindingHttpClient {
  post<T>(
    path: string,
    body?: unknown,
    options?: RequestOptions,
  ): Promise<JsonResult<T>>;
  delete<T>(path: string, options?: RequestOptions): Promise<JsonResult<T>>;
}

export interface BindingResult<T> {
  data: T;
  requestId: string;
}

function contractError(result: JsonResult<unknown>, target: string): HttpError {
  return new HttpError({
    httpStatus: result.httpStatus,
    code: "INTERNAL_ERROR",
    message: `${target}响应不符合DMS接口契约`,
    details: null,
    requestId: result.requestId,
  });
}

function assertId(value: unknown, label: string): asserts value is string {
  if (!isStringId(value)) {
    throw new TypeError(`${label}必须是十进制字符串ID`);
  }
}

export function createBindingApi(client: BindingHttpClient = httpClient) {
  return {
    async bind(
      mainPlanId: string,
      request: BindAttachmentsRequest,
      signal?: AbortSignal,
    ): Promise<BindingResult<BindAttachmentsResult>> {
      assertId(mainPlanId, "主案ID");
      if (!isBindAttachmentsRequest(request)) {
        throw new TypeError("挂载请求不符合DMS接口契约");
      }
      const result = await client.post<unknown>(
        `main-plans/${mainPlanId}/attachments/bind`,
        request,
        { signal },
      );
      if (!isBindAttachmentsResult(result.data)) {
        throw contractError(result, "挂载附件");
      }
      return { data: result.data, requestId: result.requestId };
    },

    async unbind(
      mainPlanId: string,
      attachmentId: string,
      mainPlanRowVersion: number,
      signal?: AbortSignal,
    ): Promise<BindingResult<UnbindAttachmentResult>> {
      assertId(mainPlanId, "主案ID");
      assertId(attachmentId, "附件ID");
      if (!Number.isInteger(mainPlanRowVersion) || mainPlanRowVersion < 0) {
        throw new TypeError("主案版本必须是非负整数");
      }
      const result = await client.delete<unknown>(
        `main-plans/${mainPlanId}/attachments/${attachmentId}`,
        { query: { mainPlanRowVersion }, signal },
      );
      if (!isUnbindAttachmentResult(result.data)) {
        throw contractError(result, "解除挂载");
      }
      return { data: result.data, requestId: result.requestId };
    },
  };
}

export const bindingApi = createBindingApi();
export type BindingApi = ReturnType<typeof createBindingApi>;

