import fetch, { Response } from "node-fetch";
import ora from "ora";
import logger from "./logger";

export interface AttioObject {
  id: {
    workspace_id: string;
    object_id: string;
  };
  api_slug: string;
  singular_noun: string;
  plural_noun: string;
  created_at: string;
  updated_at?: string;
}

export interface AttioAttribute {
  id: {
    workspace_id: string;
    object_id: string;
    attribute_id: string;
  };
  title: string;
  description: string | null;
  api_slug: string;
  type: string;
  is_system_attribute: boolean;
  is_writable: boolean;
  is_required: boolean;
  is_unique: boolean;
  is_multiselect: boolean;
  is_default_value_enabled: boolean;
  is_archived: boolean;
  default_value: any;
  relationship: any;
  config: Record<string, any>;
  created_at: string;
}

class HTTPResponseError extends Error {
  constructor(public response: Response) {
    super(`HTTP Error Response: ${response.status} ${response.statusText}`);
    this.response = response;
  }
}

async function logError(error: unknown): Promise<void> {
  if (error instanceof HTTPResponseError) {
    const body = await error.response.json();
    logger.error(`API error (${error.response.status}): ${JSON.stringify(body)}`);
  } else {
    logger.error(`Error fetching schema`, { error });
  }
}

async function checkStatus<R>(response: Response): Promise<R> {
  if (response.ok) {
    return response.json() as R;
  }

  throw new HTTPResponseError(response);
}

async function fetchAttioObjects(apiKey: string): Promise<AttioObject[]> {
  const fetchLoader = ora("Fetching Attio objects").start();

  try {
    const response = await fetch("https://api.attio.com/v2/objects", {
      method: "GET",
      headers: {
        Authorization: `Bearer ${apiKey}`,
        "Content-Type": "application/json",
      },
    });
    const body = await checkStatus<{ data: AttioObject[] }>(response);

    if (!response.ok) {
      logger.error(`Failed to fetch objects: ${response.statusText}`, {
        response: body,
      });
      throw new Error(`Failed to fetch objects: ${response.statusText}`);
    }

    const objects = body.data;
    fetchLoader.succeed(`Found ${objects.length} objects`);
    return objects;
  } catch (error) {
    fetchLoader.fail("Failed to fetch Attio objects");
    await logError(error);
    throw error;
  }
}

async function fetchAttioAttributes(apiKey: string, object: AttioObject): Promise<AttioAttribute[]> {
  const fetchLoader = ora(`Fetching attributes for ${object.singular_noun}`).start();

  try {
    const response = await fetch(`https://api.attio.com/v2/objects/${object.api_slug}/attributes`, {
      method: "GET",
      headers: {
        Authorization: `Bearer ${apiKey}`,
        "Content-Type": "application/json",
      },
    });

    const body = await checkStatus<{ data: AttioAttribute[] }>(response);
    const attributes = body.data;
    fetchLoader.succeed(`Found ${attributes.length} attributes for ${object.singular_noun}`);
    return attributes;
  } catch (error) {
    fetchLoader.fail(`Failed to fetch attributes for ${object.singular_noun}`);
    await logError(error);
    throw error;
  }
}

export async function fetchAttioSchema(apiKey: string) {
  const fetchLoader = ora("Fetching Attio schema").start();
  const objects = await fetchAttioObjects(apiKey);

  const attributes: Record<string, AttioAttribute[]> = {};

  for (const object of objects) {
    const objectAttributes = await fetchAttioAttributes(apiKey, object);
    attributes[object.api_slug] = objectAttributes;
  }

  fetchLoader.succeed("Attio schema fetched successfully");

  return {
    objects,
    attributes,
  };
}
