import {
  CodegenRequestBody,
  CodegenResponseBody,
  GraphqlRequestBody,
  InitRequestBody,
  InitData,
  RequestType,
  TracedFetch,
  sdkVersion,
  ObjectValue,
  JsLanguage,
  InitQuery,
  HashData,
} from "../shared";
import { parseHashData } from "../shared/helpers/hashDataEncoding";
import parseInitResponse from "./parseInitResponse";

export async function codegenRequest({
  traceId,
  token,
  branchName,
  body,
  language,
  edgeBaseUrl,
  tracedFetch,
}: {
  traceId: string;
  token: string;
  branchName: string | null;
  body: Omit<CodegenRequestBody, "sdkType" | "sdkVersion" | "language">;
  language: JsLanguage;
  edgeBaseUrl: string;
  tracedFetch: TracedFetch;
}): Promise<CodegenResponseBody> {
  const fullBody: CodegenRequestBody = {
    ...body,
    sdkType: "js",
    sdkVersion,
    language,
  };

  const responseString = await edgeRequest({
    traceId,
    token,
    branchName,
    edgeBaseUrl,
    tracedFetch,
    requestType: "codegen",
    body: fullBody,
  });

  const response = JSON.parse(responseString) as CodegenResponseBody;
  if (!response.files) {
    throw new Error(`[codegenRequest] unexpected response: ${responseString}`);
  }

  return response;
}

export async function initRequest({
  traceId,
  token,
  query,
  variables,
  branchName,
  edgeBaseUrl,
  tracedFetch,
}: {
  traceId: string;
  token: string;
  query: InitQuery;
  variables: ObjectValue;
  branchName: string | null;
  edgeBaseUrl: string;
  tracedFetch: TracedFetch;
}): Promise<InitData> {
  const body: InitRequestBody = {
    query,
    variables,
    sdkType: "js",
    sdkVersion,
  };

  const responseString = await edgeRequest({
    traceId,
    token,
    branchName,
    body,
    edgeBaseUrl,
    tracedFetch,
    requestType: "init",
  });

  const response = parseInitResponse(responseString);

  if (!response.commitId) {
    throw new Error(`[initRequest] unexpected response: ${responseString}`);
  }

  return response;
}

export async function hashRequest({
  traceId,
  token,
  query,
  variables,
  branchName,
  edgeBaseUrl,
  tracedFetch,
}: {
  traceId: string;
  token: string;
  query: InitQuery;
  variables: ObjectValue;
  branchName: string | null;
  edgeBaseUrl: string;
  tracedFetch: TracedFetch;
}): Promise<HashData> {
  const body: InitRequestBody = {
    query,
    variables,
    sdkType: "js",
    sdkVersion,
  };

  const responseString = await edgeRequest({
    traceId,
    token,
    branchName,
    body,
    edgeBaseUrl,
    tracedFetch,
    requestType: "hash",
  });

  const data = parseHashData(responseString);
  if (!data) {
    throw new Error(`[hashRequest] unexpected response: ${responseString}`);
  }
  return data;
}

async function edgeRequest({
  traceId,
  token,
  branchName,
  requestType,
  body,
  edgeBaseUrl,
  tracedFetch,
}: {
  traceId: string;
  token: string;
  branchName: string | null;
  requestType: RequestType;
  body: CodegenRequestBody | InitRequestBody;
  edgeBaseUrl: string;
  tracedFetch: TracedFetch;
}): Promise<string> {
  const url = getEdgeRequestUrl({
    baseUrl: edgeBaseUrl,
    requestType,
    token,
    branchName,
    body,
  });

  const response = await tracedFetch(traceId, url, {
    method: "GET",
    headers: { "Cache-Control": "no-store" },
  });

  return response.text();
}

export function getEdgeRequestUrl({
  baseUrl,
  requestType,
  token,
  branchName,
  body,
}: {
  baseUrl: string;
  requestType: RequestType;
  token: string;
  branchName: string | null;
  body: CodegenRequestBody | InitRequestBody | GraphqlRequestBody;
}): string {
  return `${baseUrl}/${requestType}?token=${encodeURIComponent(token)}${branchName ? `&branch=${encodeURIComponent(branchName)}` : ""}&body=${encodeURIComponent(JSON.stringify(body))}`;
}
