/**
 * Composio Service — wraps the Composio SDK for OAuth management + tool execution.
 * Single API key for the app, user isolation via entity mapping.
 */


let Composio = null;
let composioClient = null;
let sdkReady = null; // resolved promise once SDK is loaded

// Eagerly attempt to load the SDK — store the promise so getClient can await it
sdkReady = (async () => {
  try {
    const mod = await import('composio-core');
    Composio = mod.Composio || mod.default;
  } catch {
    // SDK not installed — composio features will be unavailable
  }
})();

/**
 * Lazy-init the Composio client singleton.
 * Returns null if COMPOSIO_API_KEY is not set.
 */
async function getClient() {
  if (composioClient) return composioClient;
  if (!process.env.COMPOSIO_API_KEY) return null;

  // Wait for SDK to finish loading
  await sdkReady;

  try {
    if (!Composio) return null;
    composioClient = new Composio({ apiKey: process.env.COMPOSIO_API_KEY });
    return composioClient;
  } catch {
    return null;
  }
}

/**
 * Map internal user ID to Composio entity ID.
 */
function composioUserId(internalId) {
  return `upfyn_user_${internalId}`;
}

/**
 * Get or create an entity for the given user.
 */
async function getEntity(userId) {
  const client = await getClient();
  if (!client) throw new Error('Composio not configured');
  return client.getEntity(composioUserId(userId));
}

/**
 * Initiate an OAuth connection for a user.
 * OAuth credentials are managed on the Composio dashboard — not injected here.
 *
 * @param {number} userId - Internal user ID
 * @param {string} appName - Composio app name (e.g., 'GMAIL', 'SLACK')
 * @param {string|null} authConfigId - Optional specific auth config
 * @returns {{ redirectUrl: string, connectedAccountId: string }}
 */
async function initiateConnection(userId, appName, authConfigId = null) {
  const entity = await getEntity(userId);

  const params = { appName };
  if (authConfigId) params.authConfigId = authConfigId;

  const connectionRequest = await entity.initiateConnection(params);
  return {
    redirectUrl: connectionRequest.redirectUrl,
    connectedAccountId: connectionRequest.connectedAccountId,
  };
}

/**
 * Wait/poll for a connection to complete.
 * @param {string} connectedAccountId - The connection ID from initiateConnection
 * @returns {{ status: string, appName: string }}
 */
async function waitForConnection(connectedAccountId) {
  const client = await getClient();
  if (!client) throw new Error('Composio not configured');

  const connection = await client.connectedAccounts.get({ connectedAccountId });
  return {
    status: connection.status,
    appName: connection.appName,
    id: connection.id,
  };
}

/**
 * List connected accounts for a user.
 * @param {number} userId
 * @returns {Array<{ id, appName, status, createdAt }>}
 */
async function listConnectedAccounts(userId) {
  const client = await getClient();
  if (!client) throw new Error('Composio not configured');

  const entityId = composioUserId(userId);
  try {
    const accounts = await client.connectedAccounts.list({ entityId });
    return (accounts.items || accounts || []).map(a => ({
      id: a.id,
      appName: a.appName,
      status: a.status,
      createdAt: a.createdAt,
    }));
  } catch {
    return [];
  }
}

/**
 * Get available tools for given toolkits.
 * @param {string[]} apps - e.g., ['GMAIL', 'SLACK']
 * @returns {Array<{ name, description, parameters }>}
 */
async function getTools(apps = []) {
  const client = await getClient();
  if (!client) throw new Error('Composio not configured');

  const tools = await client.actions.list({ apps });
  return (tools.items || tools || []).map(t => ({
    name: t.name,
    displayName: t.displayName || t.name,
    description: t.description,
    parameters: t.parameters,
    appName: t.appName,
  }));
}

/**
 * Get schema for a specific tool.
 * @param {string} actionName - e.g., 'GMAIL_SEND_EMAIL'
 * @returns {{ name, description, parameters }}
 */
async function getToolSchema(actionName) {
  const client = await getClient();
  if (!client) throw new Error('Composio not configured');

  const action = await client.actions.get({ actionName });
  return {
    name: action.name,
    displayName: action.displayName || action.name,
    description: action.description,
    parameters: action.parameters,
    appName: action.appName,
  };
}

/**
 * Execute a Composio tool action.
 * @param {number} userId - Internal user ID
 * @param {string} actionName - e.g., 'GMAIL_SEND_EMAIL'
 * @param {object} params - Action parameters
 * @returns {{ success, data, error }}
 */
async function executeTool(userId, actionName, params = {}) {
  const entity = await getEntity(userId);

  try {
    const result = await entity.execute(actionName, params);
    return { success: true, data: result };
  } catch (err) {
    return { success: false, error: err.message };
  }
}

/**
 * Disconnect an account.
 * @param {string} connectedAccountId
 */
async function disconnectAccount(connectedAccountId) {
  const client = await getClient();
  if (!client) throw new Error('Composio not configured');

  await client.connectedAccounts.delete({ connectedAccountId });
}

/**
 * Check if Composio is available and configured.
 */
async function isAvailable() {
  await sdkReady;
  return !!(Composio && process.env.COMPOSIO_API_KEY);
}

export {
  isAvailable,
  composioUserId,
  initiateConnection,
  waitForConnection,
  listConnectedAccounts,
  getTools,
  getToolSchema,
  executeTool,
  disconnectAccount,
};
