import { AttioObject } from "../../helpers/fetchAttioSchema";
import { writeGeneratedFile } from "../../helpers/fs";
import { generateFileHeader } from "../types/fileHeader";

const FILE_NAME = "attioClient.ts";

export function generateAttioClient(outputDir: string, objects: AttioObject[], includeStandardTypes: boolean): void {
  const standardTypesImports = includeStandardTypes
    ? `import { AttioObjectFetcher } from "./fetchers/object";
import { AttioAttributeFetcher } from "./fetchers/attribute";
import { AttioListFetcher } from "./fetchers/list";
import { AttioListEntryFetcher } from "./fetchers/listEntry";
import { AttioNoteFetcher } from "./fetchers/note";
import { AttioTaskFetcher } from "./fetchers/task";
import { AttioWebhookFetcher } from "./fetchers/webhook";
import { AttioCommentFetcher } from "./fetchers/comment";
import { AttioWorkspaceMemberFetcher } from "./fetchers/workspaceMember";`
    : "";

  const standardTypes = includeStandardTypes
    ? `objects: AttioObjectFetcher;
  attributes: AttioAttributeFetcher;
  lists: AttioListFetcher;
  listEntries: AttioListEntryFetcher;
  notes: AttioNoteFetcher;
  tasks: AttioTaskFetcher;
  webhooks: AttioWebhookFetcher;
  comments: AttioCommentFetcher;
  workspaceMembers: AttioWorkspaceMemberFetcher;`
    : "";

  const content = `${generateFileHeader(FILE_NAME)}
import fetch, { RequestInit, Response } from "node-fetch";
${standardTypesImports}
// We always import the record fetcher because it is used for all objects
import { AttioRecordFetcher } from "./fetchers/record";
import { ${objects.map((object) => `${object.singular_noun}, ${object.singular_noun}Attributes, ${object.singular_noun}InputAttributes`).join(", ")} } from "./types";

const DEFAULT_ATTIO_API_URL = "https://api.attio.com/v2";

export interface AttioClientOptions {
  /**
   * The API key for authenticating with the Attio API.
   */
  apiKey: string;

  /**
   * The base URL for the Attio API. Defaults to "https://api.attio.com/v2".
   */
  apiUrl?: string;
}

type DoFetchOptions = Omit<RequestInit, "body"> & {
  query?: object;
  body?: Record<string, any>;
};

export class AttioClient {

  private static options: AttioClientOptions;

  constructor(options: AttioClientOptions) {
    AttioClient.options = options;
    AttioClient.options.apiUrl = AttioClient.options.apiUrl || DEFAULT_ATTIO_API_URL;

    ${
      includeStandardTypes
        ? `
    this.objects = new AttioObjectFetcher(this);
    this.attributes = new AttioAttributeFetcher(this);
    this.lists = new AttioListFetcher(this);
    this.listEntries = new AttioListEntryFetcher(this);
    this.notes = new AttioNoteFetcher(this);
    this.tasks = new AttioTaskFetcher(this);
    this.webhooks = new AttioWebhookFetcher(this);
    this.comments = new AttioCommentFetcher(this);
    this.workspaceMembers = new AttioWorkspaceMemberFetcher(this)`.trim()
        : ""
    }

    ${objects.map((object) => `this.${object.api_slug} = new AttioRecordFetcher<${object.singular_noun}, ${object.singular_noun}InputAttributes, ${object.singular_noun}Attributes>(this, "${object.api_slug}");`).join("\n    ")}
  }

  public async doFetch(uri: string, opts?: DoFetchOptions): Promise<Response> {
    const { query, body, ...init } = opts || {};
    const queryString = query
      ? \`?$\{new URLSearchParams(
          Object.fromEntries(
            Object.entries(query)
              .filter(([_, v]) => v !== undefined)
              .map(([k, v]) => [k, String(v)])
          )
        ).toString()}\`
      : "";

    const response = await fetch(\`$\{AttioClient.options.apiUrl}$\{uri}$\{queryString}\`, {
      ...init,
      body: body ? JSON.stringify(body) : undefined,
      headers: {
        ...init?.headers,
        Authorization: \`Bearer $\{AttioClient.options.apiKey}\`,
        "Content-Type": "application/json",
      },
    });

    return response;
  }

  ${standardTypes}
  ${objects
    .map((object) => `${object.api_slug}: AttioRecordFetcher<${object.singular_noun}, ${object.singular_noun}InputAttributes, ${object.singular_noun}Attributes>;`)
    .join("\n  ")
    .trim()}
}
`;

  writeGeneratedFile(outputDir, FILE_NAME, content);
}
