import type {
  IntegrationProvider,
  CardData,
  TestResult,
  PushResult,
  ExternalField,
  JsonValue,
} from "../types";

const AIRTABLE_API = "https://api.airtable.com/v0";

interface AirtableConfig {
  personalAccessToken: string;
  baseId: string;
  tableIdOrName: string;
}

function parseConfig(config: JsonValue): AirtableConfig {
  const c = config as Record<string, unknown>;
  return {
    personalAccessToken: (c.personalAccessToken as string) || "",
    baseId: (c.baseId as string) || "",
    tableIdOrName: (c.tableIdOrName as string) || "",
  };
}

async function airtableFetch(
  token: string,
  path: string,
  options: RequestInit = {}
) {
  const res = await fetch(`${AIRTABLE_API}${path}`, {
    ...options,
    headers: {
      Authorization: `Bearer ${token}`,
      "Content-Type": "application/json",
      ...(options.headers || {}),
    },
  });
  if (!res.ok) {
    const text = await res.text().catch(() => "");
    throw new Error(`Airtable API ${res.status}: ${text}`);
  }
  return res.json();
}

export const airtableProvider: IntegrationProvider = {
  id: "airtable",
  name: "Airtable",
  description: "Push response cards as rows in Airtable bases",
  icon: "airtable",
  category: "spreadsheet",
  supportsFieldMapping: true,
  supportsOAuth: false,
  configFields: [
    {
      key: "personalAccessToken",
      label: "Personal Access Token",
      type: "password",
      required: true,
      helpText: "Create at airtable.com/create/tokens",
    },
    {
      key: "baseId",
      label: "Base ID",
      type: "text",
      required: true,
      placeholder: "appXXXXXXXXXXXXXX",
      helpText: "Found in the Airtable API docs for your base",
    },
    {
      key: "tableIdOrName",
      label: "Table Name or ID",
      type: "text",
      required: true,
      placeholder: "Response Cards",
    },
  ],

  setupGuide: {
    summary:
      "Airtable uses Personal Access Tokens (PATs) with explicit scopes. You'll create a token scoped to the specific base you want Echo OCR to write rows into, then paste that token with the base ID and table name.",
    docsUrl: "https://airtable.com/developers/web/guides/personal-access-tokens",
    steps: [
      {
        title: "Create a Personal Access Token",
        body: "Go to Airtable's developer hub and click \"Create token\". Give it a descriptive name like \"Echo OCR\".",
        linkUrl: "https://airtable.com/create/tokens",
        linkLabel: "Create a token",
      },
      {
        title: "Add the required scopes",
        body: "Under Scopes, add data.records:read and data.records:write. If you plan to let Echo OCR auto-detect your table schema, also add schema.bases:read.",
      },
      {
        title: "Grant access to your base",
        body: "Under \"Access\", add the specific base you want Echo OCR to write to. Avoid granting access to \"All workspaces\" — scope it narrowly.",
      },
      {
        title: "Copy the token",
        body: "Click Create token, then copy the token that appears (it starts with pat...). You won't be able to see it again after closing the dialog.",
      },
      {
        title: "Find the base ID",
        body: "Open the base in a browser and click Help → API documentation. The base ID is shown at the top of the API docs page and starts with \"app\".",
        linkUrl: "https://airtable.com/developers/web/api/introduction",
        linkLabel: "Airtable API introduction",
      },
      {
        title: "Enter the table name",
        body: "Use the exact table name as it appears in Airtable (case-sensitive) or the table ID. Echo OCR appends a new row for every card pushed.",
      },
    ],
  },

  async testConnection(config: JsonValue): Promise<TestResult> {
    try {
      const { personalAccessToken, baseId, tableIdOrName } =
        parseConfig(config);
      if (!personalAccessToken || !baseId || !tableIdOrName) {
        return {
          success: false,
          message: "Token, base ID, and table name are all required",
        };
      }
      const data = await airtableFetch(
        personalAccessToken,
        `/${baseId}/${encodeURIComponent(tableIdOrName)}?maxRecords=1`
      );
      return {
        success: true,
        message: `Connected to table (${data.records?.length ?? 0} sample records)`,
      };
    } catch (err) {
      return {
        success: false,
        message: err instanceof Error ? err.message : "Connection failed",
      };
    }
  },

  async getExternalFields(config: JsonValue): Promise<ExternalField[]> {
    const { personalAccessToken, baseId, tableIdOrName } =
      parseConfig(config);

    const data = await airtableFetch(
      personalAccessToken,
      `/${baseId}/${encodeURIComponent(tableIdOrName)}?maxRecords=1`
    );

    if (data.records?.[0]?.fields) {
      return Object.keys(data.records[0].fields).map((key) => ({
        id: key,
        name: key,
        type: typeof data.records[0].fields[key],
      }));
    }

    return [];
  },

  async pushCard(
    card: CardData,
    config: JsonValue,
    mapping: JsonValue
  ): Promise<PushResult> {
    try {
      const { personalAccessToken, baseId, tableIdOrName } =
        parseConfig(config);
      const fieldMap = (mapping as Record<string, string>) || {};

      const fields: Record<string, unknown> = {};
      for (const [cardField, airtableField] of Object.entries(fieldMap)) {
        if (!airtableField || cardField.startsWith("_")) continue;
        const val = card[cardField];
        if (val !== null && val !== undefined) {
          fields[airtableField] = typeof val === "object" ? JSON.stringify(val) : val;
        }
      }

      if (Object.keys(fields).length === 0 && card.name) {
        fields["Name"] = card.name;
        if (card.email) fields["Email"] = card.email;
        if (card.cellPhone) fields["Phone"] = card.cellPhone;
      }

      const data = await airtableFetch(
        personalAccessToken,
        `/${baseId}/${encodeURIComponent(tableIdOrName)}`,
        {
          method: "POST",
          body: JSON.stringify({ records: [{ fields }] }),
        }
      );

      const recordId = data.records?.[0]?.id;
      return {
        success: true,
        externalId: recordId,
        message: `Created record ${recordId}`,
      };
    } catch (err) {
      return {
        success: false,
        message: err instanceof Error ? err.message : "Push failed",
      };
    }
  },
};
