#!/usr/bin/env bun
/**
 * Twenty CRM setup script — configure pipeline stages and custom fields.
 * Run inside the rspace container:
 *   docker exec rspace-online bun /app/scripts/twenty-setup.js
 */

const TOKEN = process.env.TWENTY_API_TOKEN;
const BASE = process.env.TWENTY_API_URL || "http://twenty-ch-server:3000";

if (!TOKEN) {
  // Fallback: read from /proc/1/environ
  const fs = require("fs");
  try {
    const env = fs.readFileSync("/proc/1/environ", "utf8");
    const match = env.split("\0").find(e => e.startsWith("TWENTY_API_TOKEN="));
    if (match) {
      var FALLBACK_TOKEN = match.split("=").slice(1).join("=");
    }
  } catch {}
  if (!FALLBACK_TOKEN) {
    console.error("TWENTY_API_TOKEN not set");
    process.exit(1);
  }
  var API_TOKEN = FALLBACK_TOKEN;
} else {
  var API_TOKEN = TOKEN;
}

async function gqlMetadata(query, variables) {
  const r = await fetch(BASE + "/metadata", {
    method: "POST",
    headers: {
      Authorization: "Bearer " + API_TOKEN,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ query, variables }),
  });
  const d = await r.json();
  if (d.errors) {
    console.error("GraphQL error:", JSON.stringify(d.errors, null, 2));
  }
  return d.data;
}

async function gqlApi(query, variables) {
  const r = await fetch(BASE + "/api/graphql", {
    method: "POST",
    headers: {
      Authorization: "Bearer " + API_TOKEN,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ query, variables }),
  });
  const d = await r.json();
  if (d.errors) {
    console.error("API GraphQL error:", JSON.stringify(d.errors, null, 2));
  }
  return d.data;
}

// ── Step 1: Find object IDs ──
async function getObjectId(nameSingular) {
  const data = await gqlMetadata(`{
    objects(paging: { first: 100 }) {
      edges {
        node { id nameSingular }
      }
    }
  }`);
  const obj = data.objects.edges.find(e => e.node.nameSingular === nameSingular);
  return obj ? obj.node.id : null;
}

// ── Step 2: Get existing fields for an object ──
async function getFields(objectId) {
  const data = await gqlMetadata(`
    query GetFields($id: UUID!) {
      object(id: $id) {
        id
        nameSingular
        fields {
          edges {
            node {
              id
              name
              label
              type
              isCustom
              options
              defaultValue
            }
          }
        }
      }
    }
  `, { id: objectId });
  return data.object;
}

// ── Step 3: Update stage field options ──
async function updateFieldOptions(fieldId, options, defaultValue) {
  const update = { options };
  if (defaultValue !== undefined) {
    update.defaultValue = defaultValue;
  }
  const data = await gqlMetadata(`
    mutation UpdateField($input: UpdateOneFieldMetadataInput!) {
      updateOneField(input: $input) {
        id
        name
        options
        defaultValue
      }
    }
  `, {
    input: {
      id: fieldId,
      update,
    },
  });
  return data ? data.updateOneField : null;
}

// ── Step 4: Create custom field ──
async function createField(objectId, fieldDef) {
  const data = await gqlMetadata(`
    mutation CreateField($input: CreateOneFieldMetadataInput!) {
      createOneField(input: $input) {
        id
        name
        label
        type
      }
    }
  `, {
    input: {
      field: {
        objectMetadataId: objectId,
        ...fieldDef,
      },
    },
  });
  return data.createOneField;
}

// ══════════════════════════════════════════
// PIPELINE STAGES
// ══════════════════════════════════════════
const PIPELINE_STAGES = [
  { label: "New Lead",       value: "NEW_LEAD",       color: "#1E90FF", position: 0 },
  { label: "Contacted",      value: "CONTACTED",      color: "#EAB308", position: 1 },
  { label: "Qualified",      value: "QUALIFIED",      color: "#F97316", position: 2 },
  { label: "Offer Sent",     value: "OFFER_SENT",     color: "#8B5CF6", position: 3 },
  { label: "Confirmed",      value: "CONFIRMED",      color: "#14B8A6", position: 4 },
  { label: "Won",            value: "WON",            color: "#22C55E", position: 5 },
  { label: "Lost / Not Now", value: "LOST_NOT_NOW",   color: "#EF4444", position: 6 },
];

// ══════════════════════════════════════════
// CUSTOM FIELDS
// ══════════════════════════════════════════
const OPPORTUNITY_FIELDS = [
  { name: "eventDatesPreferred", label: "Event Dates (preferred)", type: "DATE" },
  { name: "eventDatesFlexible",  label: "Event Dates (flexible range)", type: "TEXT" },
  { name: "groupSize",           label: "Group Size", type: "NUMBER" },
  { name: "needsAccommodation",  label: "Needs: Accommodation", type: "BOOLEAN", defaultValue: false },
  { name: "needsCatering",       label: "Needs: Catering", type: "BOOLEAN", defaultValue: false },
  { name: "needsRooms",          label: "Needs: Rooms", type: "BOOLEAN", defaultValue: false },
  { name: "needsAV",             label: "Needs: AV", type: "BOOLEAN", defaultValue: false },
  { name: "nextActionDate",      label: "Next Action Date", type: "DATE" },
  { name: "followUpDate",        label: "Follow-up Date", type: "DATE" },
  { name: "lostReason",          label: "Lost Reason", type: "TEXT" },
];

const COMPANY_FIELDS = [
  {
    name: "leadSource",
    label: "Lead Source",
    type: "SELECT",
    options: [
      { label: "Website", value: "WEBSITE", color: "#3B82F6", position: 0 },
      { label: "Referral", value: "REFERRAL", color: "#22C55E", position: 1 },
      { label: "Event", value: "EVENT", color: "#8B5CF6", position: 2 },
      { label: "Cold Outreach", value: "COLD_OUTREACH", color: "#F97316", position: 3 },
      { label: "Social Media", value: "SOCIAL_MEDIA", color: "#06B6D4", position: 4 },
      { label: "Other", value: "OTHER", color: "#6B7280", position: 5 },
    ],
  },
  { name: "lastTouchDate", label: "Last Touch Date", type: "DATE" },
];

// ══════════════════════════════════════════
// MAIN
// ══════════════════════════════════════════
(async () => {
  console.log("=== Twenty CRM Setup ===\n");

  // 1. Get object IDs
  const oppId = await getObjectId("opportunity");
  const compId = await getObjectId("company");
  console.log("Opportunity object:", oppId);
  console.log("Company object:", compId);

  if (!oppId || !compId) {
    console.error("Could not find opportunity or company objects");
    process.exit(1);
  }

  // 2. Get current opportunity fields
  const oppObj = await getFields(oppId);
  const existingOppFields = oppObj.fields.edges.map(e => e.node);
  console.log("\nExisting opportunity fields:", existingOppFields.map(f => f.name).join(", "));

  // 3. Find and update the stage field
  const stageField = existingOppFields.find(f => f.name === "stage");
  if (stageField) {
    console.log("\nCurrent stage options:", JSON.stringify(stageField.options, null, 2));
    console.log("\nUpdating pipeline stages...");
    // Default value must match one of the new option values (Twenty wraps in quotes)
    const updated = await updateFieldOptions(stageField.id, PIPELINE_STAGES, "'NEW_LEAD'");
    if (updated) {
      console.log("Pipeline stages updated:", updated.options.map(o => o.label).join(" → "));
    }
  } else {
    console.log("\nWARNING: No 'stage' field found on opportunity object");
  }

  // 4. Create custom fields on Opportunity
  console.log("\n--- Creating Opportunity custom fields ---");
  for (const fieldDef of OPPORTUNITY_FIELDS) {
    const exists = existingOppFields.find(f => f.name === fieldDef.name);
    if (exists) {
      console.log("  SKIP (exists): " + fieldDef.name);
      continue;
    }
    const input = {
      name: fieldDef.name,
      label: fieldDef.label,
      type: fieldDef.type,
      description: "",
    };
    if (fieldDef.defaultValue !== undefined) {
      input.defaultValue = fieldDef.defaultValue;
    }
    if (fieldDef.options) {
      input.options = fieldDef.options;
    }
    try {
      const created = await createField(oppId, input);
      if (created) {
        console.log("  CREATED: " + created.name + " (" + created.type + ")");
      } else {
        console.log("  FAILED: " + fieldDef.name);
      }
    } catch (e) {
      console.log("  ERROR: " + fieldDef.name + " - " + e.message);
    }
  }

  // 5. Create custom fields on Company
  console.log("\n--- Creating Company custom fields ---");
  const compObj = await getFields(compId);
  const existingCompFields = compObj.fields.edges.map(e => e.node);
  console.log("Existing company fields:", existingCompFields.map(f => f.name).join(", "));

  for (const fieldDef of COMPANY_FIELDS) {
    const exists = existingCompFields.find(f => f.name === fieldDef.name);
    if (exists) {
      console.log("  SKIP (exists): " + fieldDef.name);
      continue;
    }
    const input = {
      name: fieldDef.name,
      label: fieldDef.label,
      type: fieldDef.type,
      description: "",
    };
    if (fieldDef.options) {
      input.options = fieldDef.options;
    }
    try {
      const created = await createField(compId, input);
      if (created) {
        console.log("  CREATED: " + created.name + " (" + created.type + ")");
      } else {
        console.log("  FAILED: " + fieldDef.name);
      }
    } catch (e) {
      console.log("  ERROR: " + fieldDef.name + " - " + e.message);
    }
  }

  console.log("\n=== Setup complete ===");
})();
