/**
 * TypeScript type definitions for all Superblocks integrations.
 * These definitions are used to provide type information in API documentation.
 */
import { compareSemVer, getBasePluginId, } from "@superblocksteam/shared";
import { datasourceSdkClassByType } from "../../../const.js";
/**
 * Compare two semantic version strings.
 * Returns true if version >= minVersion.
 */
function semverGte(version, minVersion) {
    return compareSemVer(version, minVersion) >= 0;
}
/**
 * Map of plugin IDs to their TypeScript type definitions.
 * These are based on the actual classes from @superblocksteam/library.
 */
export const INTEGRATION_TYPE_DEFINITIONS = {
    // SQL Databases - most SQL databases are in VERSION_SPECIFIC_TYPE_DEFINITIONS
    // for parameterized queries support (postgres, mysql, mariadb, mssql, snowflake,
    // redshift, bigquery, athena, databricks, oracledb, cockroachdb)
    // NoSQL Databases
    mongodb: {
        className: "MongoDB",
        pluginId: "mongodb",
        description: "MongoDB NoSQL database integration",
        typeDefinition: `type MongoDBOperation =
  | "aggregate"
  | "count"
  | "deleteOne"
  | "deleteMany"
  | "distinct"
  | "find"
  | "findOne"
  | "insertOne"
  | "insertMany"
  | "listCollections"
  | "replaceOne"
  | "updateOne"
  | "updateMany";

export class MongoDB extends Integration {
  constructor(
    name: string,
    integrationId: string,
    config: {
      /** MongoDB operation */
      operation: Binding<MongoDBOperation>;
      /** Collection name (not required for listCollections) */
      collection?: Binding<string>;
      /** Query filter (JSON) - for find, findOne, updateOne, updateMany, deleteOne, deleteMany, count, distinct, replaceOne */
      query?: Binding<any>;
      /** Update document (JSON) - for updateOne, updateMany */
      update?: Binding<any>;
      /** Replacement document (JSON) - for replaceOne */
      replacement?: Binding<any>;
      /** Document to insert (JSON) - for insertOne */
      document?: Binding<any>;
      /** Documents to insert (JSON array) - for insertMany */
      documents?: Binding<any[]>;
      /** Aggregation pipeline (JSON array) - for aggregate */
      pipeline?: Binding<any[]>;
      /** Field name - for distinct */
      field?: Binding<string>;
      /** Options (JSON) */
      options?: Binding<any>;
    }
  );

  /** Execute MongoDB operation */
  async execute(): Promise<any>;
}`,
        example: `// Find documents
new MongoDB("FindUsers", mongoIntegrationId, {
  operation: "find",
  collection: "users",
  query: { status: "active" },
  options: { limit: 10 }
})

// List collections in database
new MongoDB("ListCollections", mongoIntegrationId, {
  operation: "listCollections"
})

// Replace a document
new MongoDB("ReplaceUser", mongoIntegrationId, {
  operation: "replaceOne",
  collection: "users",
  query: ({ userId }) => ({ _id: userId }),
  replacement: ({ userData }) => userData
})`,
    },
    dynamodb: {
        className: "DynamoDb",
        pluginId: "dynamodb",
        description: "Amazon DynamoDB NoSQL database integration",
        typeDefinition: `type DynamoDbAction =
  | "getItem"
  | "updateItem"
  | "putItem"
  | "deleteItem"
  | "query"
  | "scan"
  | "executeStatement"
  | "executeTransaction"
  | "listTagsOfResource"
  | "tagResource"
  | "listTables"
  | "describeTable"
  | "createTable"
  | "updateTable"
  | "deleteTable"
  | "batchWriteItem";

export class DynamoDb extends Integration {
  constructor(
    name: string,
    integrationId: string,
    config: {
      /** DynamoDB action to perform */
      action: DynamoDbAction;
      /** Request parameters as a JSON string - structure depends on the action */
      paramsJson: Binding<string>;
    }
  );

  /** Execute DynamoDB action */
  async execute(): Promise<any>;
}`,
        example: `// Get an item
new DynamoDb("GetItem", dynamoIntegrationId, {
  action: "getItem",
  paramsJson: ({ userId }) => JSON.stringify({
    TableName: "users",
    Key: { userId: { S: userId } }
  })
})

// Query items
new DynamoDb("QueryUsers", dynamoIntegrationId, {
  action: "query",
  paramsJson: JSON.stringify({
    TableName: "users",
    KeyConditionExpression: "pk = :pk",
    ExpressionAttributeValues: { ":pk": { S: "USER" } }
  })
})

// List tables
new DynamoDb("ListTables", dynamoIntegrationId, {
  action: "listTables",
  paramsJson: "{}"
})`,
    },
    // REST API (ad-hoc, no preconfigured integration)
    restapi: {
        className: "RestApi",
        pluginId: "restapi",
        description: "Ad-hoc REST API for one-off HTTP requests. For ad-hoc requests, pass the literal string 'restapi' as the integrationId and use a full URL in the url field.",
        typeDefinition: `export class RestApi extends Integration {
  constructor(
    name: string,
    /** For ad-hoc REST APIs, pass the literal string "restapi". For preconfigured integrations, pass the integration ID. */
    integrationId: string,
    config: {
      /** HTTP method: GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS */
      method: Binding<string>;
      /** URL - for ad-hoc requests use full URL (e.g., "https://api.example.com/endpoint"), for preconfigured integrations use relative path */
      url: Binding<string>;
      /** Query parameters */
      params?: { key: Binding<string>; value: Binding<string> }[];
      /** Request headers */
      headers?: { key: Binding<string>; value: Binding<string> }[];
      /** Request body (for POST, PUT, PATCH) */
      body?: Binding<string>;
      /** Body content type: jsonBody (default), rawBody, formData, or fileForm */
      bodyType?: "jsonBody" | "rawBody" | "formData" | "fileForm";
    }
  );

  /** Execute HTTP request */
  async execute(): Promise<{ data: any; status: number; headers: Record<string, string> }>;

}`,
        example: `// Ad-hoc REST API (no preconfigured integration) - use "restapi" as integrationId and FULL URL
new RestApi("CreateUser", "restapi", {
  method: "POST",
  url: "https://api.example.com/users",  // Full URL required for ad-hoc requests
  headers: [{ key: "Content-Type", value: "application/json" }],
  body: ({ requestData }) => requestData,  // API inputs don't need .value
})

// Preconfigured REST API integration - use the integration ID and RELATIVE path
new RestApi("CreateUser", myRestApiIntegrationId, {
  method: "POST",
  url: "/users",  // Relative path - appended to base URL configured in the integration
  headers: [{ key: "Content-Type", value: "application/json" }],
  body: ({ requestData }) => requestData,
})`,
    },
    // GraphQL
    // GraphQL - requires a preconfigured GraphQL integration
    // Note: Unlike REST API, there is no ad-hoc GraphQL support. You must use a preconfigured integration.
    graphql: {
        className: "GraphQL",
        pluginId: "graphql",
        description: "GraphQL API integration. Requires a preconfigured GraphQL integration - there is no ad-hoc GraphQL support like with REST APIs.",
        typeDefinition: `export class GraphQL extends Integration {
  constructor(
    name: string,
    /** ID of a preconfigured GraphQL integration - ad-hoc GraphQL is not supported */
    integrationId: string,
    config: {
      /** GraphQL query or mutation */
      query: Binding<string>;
      /** Query variables as a JSON string */
      variables?: Binding<string>;
      /** Additional headers */
      headers?: { key: Binding<string>; value: Binding<string> }[];
    }
  );

  /** Execute GraphQL request */
  async execute(): Promise<{ data: any; errors?: any[] }>;
}`,
        example: `// Requires a preconfigured GraphQL integration - pass the integration ID
new GraphQL("GetUser", graphqlIntegrationId, {
  query: \`
    query GetUser($id: ID!) {
      user(id: $id) {
        id
        name
        email
      }
    }
  \`,
  variables: ({ userId }) => JSON.stringify({ id: userId })
})`,
    },
    // GraphQL Integration (managed GraphQL integrations - uses same GraphQL class)
    graphqlintegration: {
        className: "GraphQL",
        pluginId: "graphqlintegration",
        description: "Managed GraphQL integration for third-party services. Requires a preconfigured GraphQL integration.",
        typeDefinition: `export class GraphQL extends Integration {
  constructor(
    name: string,
    /** ID of a preconfigured GraphQL integration */
    integrationId: string,
    config: {
      /** GraphQL query or mutation */
      query: Binding<string>;
      /** Query variables as a JSON string */
      variables?: Binding<string>;
      /** Additional headers */
      headers?: { key: Binding<string>; value: Binding<string> }[];
    }
  );

  /** Execute GraphQL request */
  async execute(): Promise<{ data: any; errors?: any[] }>;
}`,
        example: `new GraphQL("GetData", graphqlIntegrationId, {
  query: \`
    query GetData($id: ID!) {
      resource(id: $id) {
        id
        name
      }
    }
  \`,
  variables: ({ resourceId }) => JSON.stringify({ id: resourceId })
})`,
    },
    // Cloud Storage
    s3: {
        className: "S3",
        pluginId: "s3",
        description: "Amazon S3 storage integration",
        typeDefinition: `
type S3Action =
  | "LIST_OBJECTS"
  | "LIST_BUCKET_OBJECTS"
  | "GET_OBJECT"
  | "DELETE_OBJECT"
  | "UPLOAD_OBJECT"
  | "LIST_BUCKETS"
  | "CREATE_BUCKET"
  | "UPLOAD_MULTIPLE_OBJECTS"
  | "GENERATE_PRESIGNED_URL";

/** Internal format after S3 plugin conversion - you don't need to construct this manually */
type S3FileObject = { name: string; contents: string; type: string };

export class S3 extends Integration {
  constructor(
    name: string,
    integrationId: string,
    config: {
      action: S3Action;
      resource?: Binding<string>;
      path?: Binding<string>;
      /** For UPLOAD_OBJECT: file content as string */
      body?: Binding<string>;
      /** For UPLOAD_MULTIPLE_OBJECTS: pass file objects directly from input.
       * Example: fileObjects: ({ files }) => files.files
       * The S3 plugin internally calls readContents() and converts to the required format. */
      fileObjects?: Binding<S3FileObject[]>;
      listFilesConfig?: {
        prefix?: Binding<string>;
        delimiter?: Binding<string>;
      };
      /** Custom properties for GENERATE_PRESIGNED_URL */
      custom?: {
        /** HTTP method for presigned URL: "PRESIGNED_METHOD_GET" for download, "PRESIGNED_METHOD_PUT" for upload. Defaults to GET. */
        presignedMethod?: "PRESIGNED_METHOD_UNSPECIFIED" | "PRESIGNED_METHOD_GET" | "PRESIGNED_METHOD_PUT";
        /** Expiration time in seconds. Defaults to 600 (10 minutes). */
        presignedExpiration?: { value: number };
      };
    }
  );

  /** Execute S3 operation */
  async execute(): Promise<any>;
}`,
        example: `// Upload file directly
new S3("UploadFile", s3IntegrationId, {
  action: "UPLOAD_OBJECT",
  resource: ({ bucketName }) => bucketName,
  path: ({ fileName }) => \`uploads/\${fileName}\`,
  body: ({ fileContent }) => fileContent
})

// Upload multiple files - fileObjects must return an array, NOT a string
new S3("UploadFiles", s3IntegrationId, {
  action: "UPLOAD_MULTIPLE_OBJECTS",
  resource: ({ bucket }) => bucket,
  fileObjects: ({ files }) => files.files  // Direct reference, no template literals!
})

// Generate presigned URL for client-side upload (best practice for large files)
new S3("GetUploadUrl", s3IntegrationId, {
  action: "GENERATE_PRESIGNED_URL",
  resource: "my-bucket",
  path: ({ fileName }) => \`uploads/\${fileName}\`,
  custom: {
    presignedMethod: "PRESIGNED_METHOD_PUT",  // PUT for upload, GET for download
    presignedExpiration: { value: 600 }
  }
})`,
    },
    // Google Cloud Storage
    gcs: {
        className: "GoogleCloudStorage",
        pluginId: "gcs",
        description: "Google Cloud Storage integration",
        typeDefinition: `
type GCSAction =
  | "LIST_OBJECTS"
  | "GET_OBJECT"
  | "DELETE_OBJECT"
  | "UPLOAD_OBJECT"
  | "LIST_BUCKETS"
  | "CREATE_BUCKET"
  | "UPLOAD_MULTIPLE_OBJECTS"
  | "GENERATE_PRESIGNED_URL";

/** Internal format after GCS plugin conversion - you don't need to construct this manually */
type GCSFileObject = { name: string; contents: string; type: string };

export class GoogleCloudStorage extends Integration {
  constructor(
    name: string,
    integrationId: string,
    config: {
      action: GCSAction;
      /** Bucket name */
      resource?: Binding<string>;
      /** File path/name within the bucket */
      path?: Binding<string>;
      /** For LIST_OBJECTS or LIST_BUCKETS: filter by prefix */
      prefix?: Binding<string>;
      /** For UPLOAD_OBJECT: file content as string */
      body?: Binding<string>;
      /** For UPLOAD_MULTIPLE_OBJECTS: pass file objects directly from input.
       * Example: fileObjects: ({ files }) => files.files */
      fileObjects?: Binding<GCSFileObject[]>;
      /** For GET_OBJECT: response type */
      responseType?: "AUTO" | "JSON" | "TEXT" | "BINARY";
      /** Custom properties for GENERATE_PRESIGNED_URL */
      custom?: {
        presignedExpiration?: { value: number };
      };
    }
  );

  /** Execute GCS operation */
  async execute(): Promise<any>;
}`,
        example: `// List files in a bucket
new GoogleCloudStorage("ListFiles", gcsIntegrationId, {
  action: "LIST_OBJECTS",
  resource: "my-bucket",
  prefix: "uploads/"
})

// Read a file
new GoogleCloudStorage("ReadFile", gcsIntegrationId, {
  action: "GET_OBJECT",
  resource: "my-bucket",
  path: "data/config.json"
})

// Upload a file
new GoogleCloudStorage("UploadFile", gcsIntegrationId, {
  action: "UPLOAD_OBJECT",
  resource: ({ bucketName }) => bucketName,
  path: ({ fileName }) => \`uploads/\${fileName}\`,
  body: ({ fileContent }) => fileContent
})

// Upload multiple files
new GoogleCloudStorage("UploadFiles", gcsIntegrationId, {
  action: "UPLOAD_MULTIPLE_OBJECTS",
  resource: ({ bucket }) => bucket,
  fileObjects: ({ files }) => files.files
})`,
    },
    // Email
    email: {
        className: "Email",
        pluginId: "email",
        description: "Email sending integration",
        typeDefinition: `export class Email extends Integration {
  constructor(
    name: string,
    integrationId: string,
    config: {
      /** Recipients (comma-separated) */
      to: Binding<string>;
      /** Email subject */
      subject: Binding<string>;
      /** Email body */
      body: Binding<string>;
      /** CC recipients (comma-separated) */
      cc?: Binding<string>;
      /** BCC recipients (comma-separated) */
      bcc?: Binding<string>;
      /** From address */
      from?: Binding<string>;
      /** Reply-to address */
      replyTo?: Binding<string>;
      /** Body type: text or html */
      bodyType?: Binding<"text" | "html">;
    }
  );

  /** Send email */
  async execute(): Promise<{ messageId: string }>;
}`,
        example: `new Email("SendNotification", emailIntegrationId, {
  to: ({ recipientEmail }) => recipientEmail,  // API inputs don't need .value
  subject: "Important Update",
  body: ({ userName }) => \`Hello \${userName}, here is your update...\`,
  bodyType: "text"
})`,
    },
    // Programming Languages
    javascript: {
        className: "JavaScript",
        pluginId: "javascript",
        description: "JavaScript code execution",
        typeDefinition: `export class JavaScript extends Integration {
  constructor(
    name: string,
    config: {
      /** JavaScript code to execute */
      fn:  (_: State) => JsonValue;
    }
  );

  /** Execute JavaScript code */
  async execute(): Promise<any>;
}`,
        example: `new JavaScript("TransformData", {
  fn: ({ GetUsers }) => {
    const users = GetUsers.data;
    return users.map(user => ({
      ...user,
      fullName: user.firstName + ' ' + user.lastName
    }));
  }
})`,
    },
    python: {
        className: "Python",
        pluginId: "python",
        description: "Python code execution",
        typeDefinition: `export class Python extends Integration {
  constructor(
    name: string,
    config: {
      /** Python code to execute */
      fn: Binding<string>;
    }
  );

  /** Execute Python code */
  async execute(): Promise<any>;
}`,
        example: `new Python("AnalyzeData", {
  fn: \`
import pandas as pd
import json

data = GetData.data
df = pd.DataFrame(data)
summary = df.describe().to_dict()
return json.dumps(summary)
  \`
})`,
    },
    // Salesforce
    salesforce: {
        className: "Salesforce",
        pluginId: "salesforce",
        description: "Salesforce CRM integration",
        typeDefinition: `
export type SalesforceAction =
  | { action: "SOQL_ACTION_SOQL"; soqlBody: Binding<string> }
  | {
      action: "CRUD_ACTION_READ";
      resourceType: Binding<string>;
      resourceId: Binding<string>;
    }
  | {
      action: "CRUD_ACTION_CREATE";
      resourceType: Binding<string>;
      resourceBody: Binding<string>;
    }
  | {
      action: "CRUD_ACTION_UPDATE";
      resourceType: Binding<string>;
      resourceId: Binding<string>;
      resourceBody: Binding<string>;
    }
  | {
      action: "CRUD_ACTION_DELETE";
      resourceType: Binding<string>;
      resourceId: Binding<string>;
      resourceBody: Binding<string>;
    }
  | {
      action: "BULK_ACTION_CREATE";
      resourceType: Binding<string>;
      resourceBody: Binding<string>;
    }
  | {
      action: "BULK_ACTION_UPDATE";
      resourceType: Binding<string>;
      resourceBody: Binding<string>;
    };

export declare class Salesforce extends Integration {
  constructor(
    name: string,
    integration: string,
    config: {
      action: SalesforceAction;
    },
  );

  /** Execute Salesforce action
   * SOQL queries return an array of records directly (NOT wrapped in { records: [...] })
   * CRUD actions return the affected record(s)
   */
  async execute(): Promise<any[]>;
}
`,
        example: `// SOQL Query - returns array directly
new Salesforce("GetAccounts", salesforceIntegrationId, {
  action: {
    action: "SOQL_ACTION_SOQL",
    soqlBody: "SELECT Id, Name FROM Account WHERE Industry = 'Technology'"
  }
})
// Output: [{ Id: "001...", Name: "Acme" }, { Id: "001...", Name: "TechCorp" }]
// Access: GetAccounts.output is the array directly, use Array.isArray() to check`,
    },
    // Generic OpenAPI
    restapiintegration: {
        className: "OpenApi",
        pluginId: "restapiintegration",
        description: "Generic OpenApi-based integration",
        typeDefinition: `export class OpenApi extends RestApi {
  constructor(
    name: string,
    integrationId: string,
    config: {
      /** HTTP method: GET, POST, PUT, PATCH, DELETE */
      method: string;
      /** API endpoint path */
      url: Binding<string>; // This should match openapi.path and just be the path, NOT a full URL
      /** Request headers */
      headers?: { key: Binding<string>; value: Binding<string> }[];
      /** Query parameters */
      params?: { key: Binding<string>; value: Binding<string> }[];
      /** Request body (for POST, PUT, PATCH) */
      body?: Binding<string>;
      /** Body content type: jsonBody (default), rawBody, formData, or fileForm */
      bodyType?: "jsonBody" | "rawBody" | "formData" | "fileForm";
    }
    openapi?: {
      /** Endpoint path from OpenApi spec */
      path: string;
    },
  );

  /** Execute HTTP request */
  async execute(): Promise<{ data: any; status: number; headers: Record<string, string> }>;
}`,
        example: `new OpenApi("CallEndpoint", integrationId, {
  method: "POST",
  path: "/api/v1/resource",
  headers: [{ key: "Content-Type", value: "application/json" }],
  body: ({ requestData }) => requestData,
})`,
    },
    // Add more integrations as needed...
};
/**
 * Version-specific type definitions for plugins that have different APIs based on version.
 * The key format is "pluginId:minVersion" where minVersion is the minimum version for that definition.
 * Definitions are checked in order, so more specific versions should come first.
 */
export const VERSION_SPECIFIC_TYPE_DEFINITIONS = [
    // PostgreSQL 0.0.12+ uses run_sql with parameters
    {
        pluginId: "postgres",
        minVersion: "0.0.12",
        definition: {
            className: "PostgreSQL",
            pluginId: "postgres",
            description: "PostgreSQL database integration (v0.0.12+)",
            typeDefinition: `export class PostgreSQL extends Integration {
  constructor(
    name: string,
    integrationId: string,
    config: {
      statement: Binding<string>;
      /** SQL parameters array - use "" for queries without parameters */
      parameters: Binding<string>;
    }
  );

  /** Execute SQL statement - returns array of rows */
  async execute(): Promise<any[]>;
}`,
            example: `// Simple parameterized SQL - declare inputs in statement function signature
new PostgreSQL("GetUsers", postgresIntegrationId, {
  statement: ({ statusFilter, offset, limit }: { statusFilter: string; offset: number; limit: number }) =>
    \`SELECT * FROM users WHERE status = $1 OFFSET $2 LIMIT $3\`,
  parameters: "[statusFilter, offset, limit]"
})

// Mixed pattern - binding function for dynamic SQL + parameters for safe values
new PostgreSQL("GetUsersByTable", postgresIntegrationId, {
  statement: ({ tableName, statusFilter }: { tableName: string; statusFilter: string }) =>
    \`SELECT * FROM \${tableName} WHERE status = $1\`,
  parameters: "[statusFilter]"
})

// Array handling - use = ANY() for WHERE IN queries with arrays
new PostgreSQL("GetUsersByIds", postgresIntegrationId, {
  statement: ({ userIds }: { userIds: number[] }) =>
    \`SELECT * FROM users WHERE id = ANY($1::int[])\`,  // Cast to appropriate array type
  parameters: "[userIds]"  // userIds should be an array like [1, 2, 3]
})

// Dynamic ORDER BY - can't be parameterized, use empty parameters
new PostgreSQL("SortedUsers", postgresIntegrationId, {
  statement: ({ sortColumn, sortDir }: { sortColumn: string; sortDir: 'ASC' | 'DESC' }) =>
    \`SELECT * FROM users ORDER BY \${sortColumn} \${sortDir}\`,
  parameters: ""  // Empty string - ORDER BY columns/directions can't be parameterized
})`,
        },
    },
    // PostgreSQL < 0.0.12 uses run_sql with usePreparedSql
    {
        pluginId: "postgres",
        minVersion: "0.0.0",
        definition: {
            className: "PostgreSQL",
            pluginId: "postgres",
            description: "PostgreSQL database integration (legacy)",
            typeDefinition: `export class PostgreSQL extends Integration {
  constructor(
    name: string,
    integrationId: string,
    config: {
      statement: Binding<string>;
      /** Operation type - run_sql is the legacy format */
      operation?: "run_sql";
      /** Whether to use prepared statements (legacy) */
      usePreparedSql?: boolean;
    }
  );

  /** Execute SQL statement - returns array of rows */
  async execute(): Promise<any[]>;
}`,
            example: `new PostgreSQL("GetUsers", postgresIntegrationId, {
  statement: ({ statusFilter }: { statusFilter: string }) =>
    \`SELECT * FROM users WHERE status = '\${statusFilter}'\`,
  usePreparedSql: true  // Optional: use prepared statements
})`,
        },
    },
    // MySQL 0.0.12+ uses run_sql with parameters (? placeholders)
    {
        pluginId: "mysql",
        minVersion: "0.0.12",
        definition: {
            className: "MySQL",
            pluginId: "mysql",
            description: "MySQL database integration (v0.0.12+)",
            typeDefinition: `export class MySQL extends Integration {
  constructor(
    name: string,
    integrationId: string,
    config: {
      statement: Binding<string>;
      /** SQL parameters array - use "" for queries without parameters */
      parameters: Binding<string>;
    }
  );

  /** Execute SQL statement - returns array of rows */
  async execute(): Promise<any[]>;
}`,
            example: `// Simple parameterized SQL - declare inputs in statement function signature
new MySQL("GetUsers", mysqlIntegrationId, {
  statement: ({ statusFilter, offset, limit }: { statusFilter: string; offset: number; limit: number }) =>
    \`SELECT * FROM users WHERE status = ? OFFSET ? LIMIT ?\`,
  parameters: "[statusFilter, offset, limit]"
})

// Array handling - use FIND_IN_SET for WHERE IN queries with comma-separated values
new MySQL("GetUsersByIds", mysqlIntegrationId, {
  statement: ({ userIds }: { userIds: number[] }) =>
    \`SELECT * FROM users WHERE FIND_IN_SET(id, ?)\`,
  parameters: "[userIds.join(',')]"  // Convert array to comma-separated string
})

// Dynamic ORDER BY - can't be parameterized, use empty parameters
new MySQL("SortedUsers", mysqlIntegrationId, {
  statement: ({ sortColumn, sortDir }: { sortColumn: string; sortDir: 'ASC' | 'DESC' }) =>
    \`SELECT * FROM users ORDER BY \${sortColumn} \${sortDir}\`,
  parameters: ""  // Empty string - ORDER BY columns/directions can't be parameterized
})`,
        },
    },
    // MySQL < 0.0.12 uses run_sql with usePreparedSql
    {
        pluginId: "mysql",
        minVersion: "0.0.0",
        definition: {
            className: "MySQL",
            pluginId: "mysql",
            description: "MySQL database integration (legacy)",
            typeDefinition: `export class MySQL extends Integration {
  constructor(
    name: string,
    integrationId: string,
    config: {
      statement: Binding<string>;
    }
  );

  /** Execute SQL statement - returns array of rows */
  async execute(): Promise<any[]>;
}`,
            example: `new MySQL("GetOrders", mysqlIntegrationId, {
  statement: ({ customerId }) =>
    \`SELECT * FROM orders WHERE customer_id = \${customerId}\`
})`,
        },
    },
    // MariaDB 0.0.12+ uses run_sql with parameters (? placeholders)
    {
        pluginId: "mariadb",
        minVersion: "0.0.12",
        definition: {
            className: "MariaDB",
            pluginId: "mariadb",
            description: "MariaDB database integration (v0.0.12+)",
            typeDefinition: `export class MariaDB extends Integration {
  constructor(
    name: string,
    integrationId: string,
    config: {
      statement: Binding<string>;
      /** SQL parameters array - use "" for queries without parameters */
      parameters: Binding<string>;
    }
  );

  /** Execute SQL statement - returns array of rows */
  async execute(): Promise<any[]>;
}`,
            example: `// Simple parameterized SQL - declare inputs in statement function signature
new MariaDB("GetUsers", mariadbIntegrationId, {
  statement: ({ statusFilter, offset, limit }: { statusFilter: string; offset: number; limit: number }) =>
    \`SELECT * FROM users WHERE status = ? OFFSET ? LIMIT ?\`,
  parameters: "[statusFilter, offset, limit]"
})

// Array handling - use FIND_IN_SET for WHERE IN queries with comma-separated values
new MariaDB("GetUsersByIds", mariadbIntegrationId, {
  statement: ({ userIds }: { userIds: number[] }) =>
    \`SELECT * FROM users WHERE FIND_IN_SET(id, ?)\`,
  parameters: "[userIds.join(',')]"  // Convert array to comma-separated string
})

// Dynamic ORDER BY - can't be parameterized, use empty parameters
new MariaDB("SortedUsers", mariadbIntegrationId, {
  statement: ({ sortColumn, sortDir }: { sortColumn: string; sortDir: 'ASC' | 'DESC' }) =>
    \`SELECT * FROM users ORDER BY \${sortColumn} \${sortDir}\`,
  parameters: ""  // Empty string - ORDER BY columns/directions can't be parameterized
})`,
        },
    },
    // MariaDB < 0.0.12 uses run_sql with usePreparedSql
    {
        pluginId: "mariadb",
        minVersion: "0.0.0",
        definition: {
            className: "MariaDB",
            pluginId: "mariadb",
            description: "MariaDB database integration (legacy)",
            typeDefinition: `export class MariaDB extends Integration {
  constructor(
    name: string,
    integrationId: string,
    config: {
      statement: Binding<string>;
    }
  );

  /** Execute SQL statement - returns array of rows */
  async execute(): Promise<any[]>;
}`,
            example: `new MariaDB("GetOrders", mariadbIntegrationId, {
  statement: ({ customerId }) =>
    \`SELECT * FROM orders WHERE customer_id = \${customerId}\`
})`,
        },
    },
    // Microsoft SQL Server 0.0.11+ uses run_sql with parameters (@p1, @p2 placeholders)
    {
        pluginId: "mssql",
        minVersion: "0.0.11",
        definition: {
            className: "MicrosoftSql",
            pluginId: "mssql",
            description: "Microsoft SQL Server integration (v0.0.11+)",
            typeDefinition: `export class MicrosoftSql extends Integration {
  constructor(
    name: string,
    integrationId: string,
    config: {
      statement: Binding<string>;
      /** SQL parameters array - use "" for queries without parameters */
      parameters: Binding<string>;
    }
  );

  /** Execute SQL statement - returns array of rows */
  async execute(): Promise<any[]>;
}`,
            example: `// Simple parameterized SQL - declare inputs in statement function signature
new MicrosoftSql("GetUsers", mssqlIntegrationId, {
  statement: ({ statusFilter, offset, limit }: { statusFilter: string; offset: number; limit: number }) =>
    \`SELECT * FROM users WHERE status = @PARAM_1 OFFSET @PARAM_2 ROWS FETCH NEXT @PARAM_3 ROWS ONLY\`,
  parameters: "[statusFilter, offset, limit]"
})

// Array handling - use STRING_SPLIT for WHERE IN queries with comma-separated values
new MicrosoftSql("GetUsersByIds", mssqlIntegrationId, {
  statement: ({ userIds }: { userIds: number[] }) =>
    \`SELECT * FROM users WHERE id IN (SELECT value FROM STRING_SPLIT(@PARAM_1, ','))\`,
  parameters: "[userIds.join(',')]"  // Convert array to comma-separated string
})

// Dynamic ORDER BY - can't be parameterized, use empty parameters
new MicrosoftSql("SortedUsers", mssqlIntegrationId, {
  statement: ({ sortColumn, sortDir }: { sortColumn: string; sortDir: 'ASC' | 'DESC' }) =>
    \`SELECT * FROM users ORDER BY \${sortColumn} \${sortDir}\`,
  parameters: ""  // Empty string - ORDER BY columns/directions can't be parameterized
})`,
        },
    },
    // Microsoft SQL Server < 0.0.11 uses run_sql with usePreparedSql
    {
        pluginId: "mssql",
        minVersion: "0.0.0",
        definition: {
            className: "MicrosoftSql",
            pluginId: "mssql",
            description: "Microsoft SQL Server integration (legacy)",
            typeDefinition: `export class MicrosoftSql extends Integration {
  constructor(
    name: string,
    integrationId: string,
    config: {
      statement: Binding<string>;
    }
  );

  /** Execute SQL statement - returns array of rows */
  async execute(): Promise<any[]>;
}`,
            example: `new MicrosoftSql("GetProducts", mssqlIntegrationId, {
  statement: ({ minPrice }) =>
    \`SELECT * FROM products WHERE price > \${minPrice}\`
})`,
        },
    },
    // Snowflake 0.0.11+ uses run_sql with parameters (? placeholders)
    {
        pluginId: "snowflake",
        minVersion: "0.0.11",
        definition: {
            className: "Snowflake",
            pluginId: "snowflake",
            description: "Snowflake data warehouse integration (v0.0.11+)",
            typeDefinition: `export class Snowflake extends Integration {
  constructor(
    name: string,
    integrationId: string,
    config: {
      statement: Binding<string>;
      /** SQL parameters array - use "" for queries without parameters */
      parameters: Binding<string>;
    }
  );

  /** Execute SQL statement - returns array of rows */
  async execute(): Promise<any[]>;
}`,
            example: `// Simple parameterized SQL - declare inputs in statement function signature
new Snowflake("GetUsers", snowflakeIntegrationId, {
  statement: ({ statusFilter, offset, limit }: { statusFilter: string; offset: number; limit: number }) =>
    \`SELECT * FROM users WHERE status = ? OFFSET ? LIMIT ?\`,
  parameters: "[statusFilter, offset, limit]"
})

// Array handling - use ARRAY_CONTAINS with PARSE_JSON for WHERE IN queries
new Snowflake("GetUsersByCities", snowflakeIntegrationId, {
  statement: ({ selectedCities }: { selectedCities: string[] }) =>
    \`SELECT * FROM users WHERE ARRAY_CONTAINS(city::variant, PARSE_JSON(?))\`,
  parameters: "[JSON.stringify(selectedCities)]"  // Pass array as JSON string
})

// Dynamic ORDER BY - can't be parameterized, use empty parameters
new Snowflake("SortedUsers", snowflakeIntegrationId, {
  statement: ({ sortColumn, sortDir }: { sortColumn: string; sortDir: 'ASC' | 'DESC' }) =>
    \`SELECT * FROM users ORDER BY \${sortColumn} \${sortDir}\`,
  parameters: ""  // Empty string - ORDER BY columns/directions can't be parameterized
})`,
        },
    },
    // Snowflake < 0.0.11 uses legacy format
    {
        pluginId: "snowflake",
        minVersion: "0.0.0",
        definition: {
            className: "Snowflake",
            pluginId: "snowflake",
            description: "Snowflake data warehouse integration (legacy)",
            typeDefinition: `export class Snowflake extends Integration {
  constructor(
    name: string,
    integrationId: string,
    config: {
      statement: Binding<string>;
    }
  );

  /** Execute SQL statement - returns array of rows */
  async execute(): Promise<any[]>;
}`,
            example: `new Snowflake("GetSales", snowflakeIntegrationId, {
  statement: ({ region }) =>
    \`SELECT * FROM sales WHERE region = '\${region}'\`
})`,
        },
    },
    // Redshift 0.0.8+ uses run_sql with parameters ($1, $2 placeholders)
    {
        pluginId: "redshift",
        minVersion: "0.0.8",
        definition: {
            className: "Redshift",
            pluginId: "redshift",
            description: "Amazon Redshift integration (v0.0.8+)",
            typeDefinition: `export class Redshift extends Integration {
  constructor(
    name: string,
    integrationId: string,
    config: {
      statement: Binding<string>;
      /** SQL parameters array - use "" for queries without parameters */
      parameters: Binding<string>;
    }
  );

  /** Execute SQL statement - returns array of rows */
  async execute(): Promise<any[]>;
}`,
            example: `// Simple parameterized SQL - declare inputs in statement function signature
new Redshift("GetUsers", redshiftIntegrationId, {
  statement: ({ statusFilter, offset, limit }: { statusFilter: string; offset: number; limit: number }) =>
    \`SELECT * FROM users WHERE status = $1 OFFSET $2 LIMIT $3\`,
  parameters: "[statusFilter, offset, limit]"
})

// Array handling - use = ANY() for WHERE IN queries with arrays (PostgreSQL-compatible)
new Redshift("GetUsersByIds", redshiftIntegrationId, {
  statement: ({ userIds }: { userIds: number[] }) =>
    \`SELECT * FROM users WHERE id = ANY($1::int[])\`,
  parameters: "[userIds]"  // Pass array directly
})

// Dynamic ORDER BY - can't be parameterized, use empty parameters
new Redshift("SortedUsers", redshiftIntegrationId, {
  statement: ({ sortColumn, sortDir }: { sortColumn: string; sortDir: 'ASC' | 'DESC' }) =>
    \`SELECT * FROM users ORDER BY \${sortColumn} \${sortDir}\`,
  parameters: ""  // Empty string - ORDER BY columns/directions can't be parameterized
})`,
        },
    },
    // Redshift < 0.0.8 uses legacy format
    {
        pluginId: "redshift",
        minVersion: "0.0.0",
        definition: {
            className: "Redshift",
            pluginId: "redshift",
            description: "Amazon Redshift integration (legacy)",
            typeDefinition: `export class Redshift extends Integration {
  constructor(
    name: string,
    integrationId: string,
    config: {
      statement: Binding<string>;
    }
  );

  /** Execute SQL statement - returns array of rows */
  async execute(): Promise<any[]>;
}`,
            example: `new Redshift("GetAnalytics", redshiftIntegrationId, {
  statement: ({ dateRange }) =>
    \`SELECT * FROM analytics WHERE date_range = '\${dateRange}'\`
})`,
        },
    },
    // BigQuery 0.0.8+ uses run_sql with parameters (@param1, @param2 placeholders)
    {
        pluginId: "bigquery",
        minVersion: "0.0.8",
        definition: {
            className: "BigQuery",
            pluginId: "bigquery",
            description: "Google BigQuery integration (v0.0.8+)",
            typeDefinition: `export class BigQuery extends Integration {
  constructor(
    name: string,
    integrationId: string,
    config: {
      sqlBody: Binding<string>;
      /** SQL parameters array - use "" for queries without parameters */
      parameters: Binding<string>;
    }
  );

  /** Execute SQL statement - returns array of rows */
  async execute(): Promise<any[]>;
}`,
            example: `// Simple parameterized SQL - declare inputs in statement function signature
new BigQuery("GetUsers", bigqueryIntegrationId, {
  sqlBody: ({ statusFilter, offset, limit }: { statusFilter: string; offset: number; limit: number }) =>
    \`SELECT * FROM users WHERE status = ? OFFSET ? LIMIT ?\`,
  parameters: "[statusFilter, offset, limit]"
})

// Array handling - use UNNEST for WHERE IN queries with arrays
new BigQuery("GetUsersByIds", bigqueryIntegrationId, {
  sqlBody: ({ userIds }: { userIds: number[] }) =>
    \`SELECT * FROM users WHERE id IN UNNEST(?)\`,
  parameters: "[userIds]"  // Pass array directly - BigQuery handles array parameters natively
})

// Dynamic ORDER BY - can't be parameterized, use empty parameters
new BigQuery("SortedUsers", bigqueryIntegrationId, {
  sqlBody: ({ sortColumn, sortDir }: { sortColumn: string; sortDir: 'ASC' | 'DESC' }) =>
    \`SELECT * FROM users ORDER BY \${sortColumn} \${sortDir}\`,
  parameters: ""  // Empty string - ORDER BY columns/directions can't be parameterized
})`,
        },
    },
    // BigQuery < 0.0.8 uses legacy format
    {
        pluginId: "bigquery",
        minVersion: "0.0.0",
        definition: {
            className: "BigQuery",
            pluginId: "bigquery",
            description: "Google BigQuery integration (legacy)",
            typeDefinition: `export class BigQuery extends Integration {
  constructor(
    name: string,
    integrationId: string,
    config: {
      sqlBody: Binding<string>;
    }
  );

  /** Execute SQL statement - returns array of rows */
  async execute(): Promise<any[]>;
}`,
            example: `new BigQuery("GetAnalytics", bigqueryIntegrationId, {
  sqlBody: ({ dataset }) =>
    \`SELECT * FROM \${dataset}.events\`
})`,
        },
    },
    // Athena 0.0.2+ uses run_sql with parameters (? placeholders)
    {
        pluginId: "athena",
        minVersion: "0.0.2",
        definition: {
            className: "Athena",
            pluginId: "athena",
            description: "Amazon Athena integration (v0.0.2+)",
            typeDefinition: `export class Athena extends Integration {
  constructor(
    name: string,
    integrationId: string,
    config: {
      sqlBody: Binding<string>;
      /** SQL parameters array - use "" for queries without parameters */
      parameters: Binding<string>;
    }
  );

  /** Execute SQL statement - returns array of rows */
  async execute(): Promise<any[]>;
}`,
            example: `// Simple parameterized SQL - declare inputs in statement function signature
new Athena("GetLogs", athenaIntegrationId, {
  sqlBody: ({ logLevel, limit }: { logLevel: string; limit: number }) =>
    \`SELECT * FROM logs WHERE level = ? LIMIT ?\`,
  parameters: "[logLevel, limit]"
})

// Dynamic ORDER BY - can't be parameterized, use empty parameters
new Athena("SortedLogs", athenaIntegrationId, {
  sqlBody: ({ sortColumn, sortDir }: { sortColumn: string; sortDir: 'ASC' | 'DESC' }) =>
    \`SELECT * FROM logs ORDER BY \${sortColumn} \${sortDir}\`,
  parameters: ""  // Empty string - ORDER BY columns/directions can't be parameterized
})`,
        },
    },
    // Athena < 0.0.2 uses legacy format
    {
        pluginId: "athena",
        minVersion: "0.0.0",
        definition: {
            className: "Athena",
            pluginId: "athena",
            description: "Amazon Athena integration (legacy)",
            typeDefinition: `export class Athena extends Integration {
  constructor(
    name: string,
    integrationId: string,
    config: {
      sqlBody: Binding<string>;
    }
  );

  /** Execute SQL statement - returns array of rows */
  async execute(): Promise<any[]>;
}`,
            example: `new Athena("GetLogs", athenaIntegrationId, {
  sqlBody: ({ database }) =>
    \`SELECT * FROM \${database}.logs\`
})`,
        },
    },
    // Databricks 0.0.5+ uses run_sql with parameters (:param1, :param2 placeholders)
    {
        pluginId: "databricks",
        minVersion: "0.0.5",
        definition: {
            className: "Databricks",
            pluginId: "databricks",
            description: "Databricks integration (v0.0.5+)",
            typeDefinition: `export class Databricks extends Integration {
  constructor(
    name: string,
    integrationId: string,
    config: {
      statement: Binding<string>;
      /** SQL parameters array - use "" for queries without parameters */
      parameters: Binding<string>;
    }
  );

  /** Execute SQL statement - returns array of rows */
  async execute(): Promise<any[]>;
}`,
            example: `// Simple parameterized SQL - declare inputs in statement function signature
new Databricks("GetUsers", databricksIntegrationId, {
  statement: ({ statusFilter, offset, limit }: { statusFilter: string; offset: number; limit: number }) =>
    \`SELECT * FROM users WHERE status = :PARAM_1 OFFSET :PARAM_2 LIMIT :PARAM_3\`,
  parameters: "[statusFilter, offset, limit]"
})

// Array handling - use array_contains for WHERE IN queries with arrays
new Databricks("GetUsersByIds", databricksIntegrationId, {
  statement: ({ userIds }: { userIds: number[] }) =>
    \`SELECT * FROM users WHERE array_contains(array(:PARAM_1), id)\`,
  parameters: "[userIds.join(',')]"  // For dynamic arrays, may need string splitting approach
})

// Dynamic ORDER BY - can't be parameterized, use empty parameters
new Databricks("SortedUsers", databricksIntegrationId, {
  statement: ({ sortColumn, sortDir }: { sortColumn: string; sortDir: 'ASC' | 'DESC' }) =>
    \`SELECT * FROM users ORDER BY \${sortColumn} \${sortDir}\`,
  parameters: ""  // Empty string - ORDER BY columns/directions can't be parameterized
})`,
        },
    },
    // Databricks < 0.0.5 uses legacy format
    {
        pluginId: "databricks",
        minVersion: "0.0.0",
        definition: {
            className: "Databricks",
            pluginId: "databricks",
            description: "Databricks integration (legacy)",
            typeDefinition: `export class Databricks extends Integration {
  constructor(
    name: string,
    integrationId: string,
    config: {
      statement: Binding<string>;
    }
  );

  /** Execute SQL statement - returns array of rows */
  async execute(): Promise<any[]>;
}`,
            example: `new Databricks("GetAnalytics", databricksIntegrationId, {
  statement: ({ catalog }) =>
    \`SELECT * FROM \${catalog}.default.events\`
})`,
        },
    },
    // CockroachDB 0.0.3+ uses run_sql with parameters ($1, $2 placeholders - PostgreSQL-compatible)
    {
        pluginId: "cockroachdb",
        minVersion: "0.0.3",
        definition: {
            className: "CockroachDB",
            pluginId: "cockroachdb",
            description: "CockroachDB distributed SQL database (v0.0.3+)",
            typeDefinition: `export class CockroachDB extends Integration {
  constructor(
    name: string,
    integrationId: string,
    config: {
      statement: Binding<string>;
      /** SQL parameters array - use "" for queries without parameters */
      parameters: Binding<string>;
    }
  );

  /** Execute SQL statement - returns array of rows */
  async execute(): Promise<any[]>;
}`,
            example: `// Simple parameterized SQL - declare inputs in statement function signature
new CockroachDB("GetUsers", cockroachdbIntegrationId, {
  statement: ({ statusFilter, offset, limit }: { statusFilter: string; offset: number; limit: number }) =>
    \`SELECT * FROM users WHERE status = $1 OFFSET $2 LIMIT $3\`,
  parameters: "[statusFilter, offset, limit]"
})

// Array handling - use = ANY() for WHERE IN queries with arrays (PostgreSQL-compatible)
new CockroachDB("GetUsersByIds", cockroachdbIntegrationId, {
  statement: ({ userIds }: { userIds: number[] }) =>
    \`SELECT * FROM users WHERE id = ANY($1::int[])\`,  // Cast to appropriate array type
  parameters: "[userIds]"  // userIds should be an array like [1, 2, 3]
})

// Dynamic ORDER BY - can't be parameterized, use empty parameters
new CockroachDB("SortedUsers", cockroachdbIntegrationId, {
  statement: ({ sortColumn, sortDir }: { sortColumn: string; sortDir: 'ASC' | 'DESC' }) =>
    \`SELECT * FROM users ORDER BY \${sortColumn} \${sortDir}\`,
  parameters: ""  // Empty string - ORDER BY columns/directions can't be parameterized
})`,
        },
    },
    // CockroachDB < 0.0.3 uses legacy format
    {
        pluginId: "cockroachdb",
        minVersion: "0.0.0",
        definition: {
            className: "CockroachDB",
            pluginId: "cockroachdb",
            description: "CockroachDB distributed SQL database (legacy)",
            typeDefinition: `export class CockroachDB extends Integration {
  constructor(
    name: string,
    integrationId: string,
    config: {
      statement: Binding<string>;
    }
  );

  /** Execute SQL statement - returns array of rows */
  async execute(): Promise<any[]>;
}`,
            example: `new CockroachDB("GetAccounts", cockroachdbIntegrationId, {
  statement: ({ region }) =>
    \`SELECT * FROM accounts WHERE region = '\${region}'\`
})`,
        },
    },
    // Lakebase (Databricks PostgreSQL) - uses PostgreSQL-compatible $1, $2 placeholders
    {
        pluginId: "lakebase",
        minVersion: "0.0.1",
        definition: {
            className: "Lakebase",
            pluginId: "lakebase",
            description: "Databricks Lakebase PostgreSQL-compatible database",
            typeDefinition: `export class Lakebase extends Integration {
  constructor(
    name: string,
    integrationId: string,
    config: {
      statement: Binding<string>;
      /** SQL parameters array - use "" for queries without parameters */
      parameters?: Binding<string>;
    }
  );

  /** Execute SQL statement - returns array of rows */
  async execute(): Promise<any[]>;
}`,
            example: `// Simple parameterized SQL - declare inputs in statement function signature
new Lakebase("GetUsers", lakebaseIntegrationId, {
  statement: ({ statusFilter, offset, limit }: { statusFilter: string; offset: number; limit: number }) =>
    \`SELECT * FROM users WHERE status = $1 OFFSET $2 LIMIT $3\`,
  parameters: "[statusFilter, offset, limit]"
})

// Array handling - use = ANY() for WHERE IN queries with arrays (PostgreSQL-compatible)
new Lakebase("GetUsersByIds", lakebaseIntegrationId, {
  statement: ({ userIds }: { userIds: number[] }) =>
    \`SELECT * FROM users WHERE id = ANY($1::int[])\`,  // Cast to appropriate array type
  parameters: "[userIds]"  // userIds should be an array like [1, 2, 3]
})

// Dynamic ORDER BY - can't be parameterized, use empty parameters
new Lakebase("SortedUsers", lakebaseIntegrationId, {
  statement: ({ sortColumn, sortDir }: { sortColumn: string; sortDir: 'ASC' | 'DESC' }) =>
    \`SELECT * FROM users ORDER BY \${sortColumn} \${sortDir}\`,
  parameters: ""  // Empty string - ORDER BY columns/directions can't be parameterized
})`,
        },
    },
    // OracleDB 0.0.3+ uses run_sql with parameters (:1, :2 placeholders)
    {
        pluginId: "oracledb",
        minVersion: "0.0.3",
        definition: {
            className: "OracleDB",
            pluginId: "oracledb",
            description: "Oracle Database integration (v0.0.3+)",
            typeDefinition: `export class OracleDB extends Integration {
  constructor(
    name: string,
    integrationId: string,
    config: {
      statement: Binding<string>;
      /** SQL parameters array - use "" for queries without parameters */
      parameters: Binding<string>;
    }
  );

  /** Execute SQL statement - returns array of rows */
  async execute(): Promise<any[]>;
}`,
            example: `// Simple parameterized SQL - declare inputs in statement function signature
new OracleDB("GetUsers", oracledbIntegrationId, {
  statement: ({ statusFilter, offset, limit }: { statusFilter: string; offset: number; limit: number }) =>
    \`SELECT * FROM users WHERE status = :1 OFFSET :2 ROWS FETCH NEXT :3 ROWS ONLY\`,
  parameters: "[statusFilter, offset, limit]"
})

// Dynamic ORDER BY - can't be parameterized, use empty parameters
new OracleDB("SortedUsers", oracledbIntegrationId, {
  statement: ({ sortColumn, sortDir }: { sortColumn: string; sortDir: 'ASC' | 'DESC' }) =>
    \`SELECT * FROM users ORDER BY \${sortColumn} \${sortDir}\`,
  parameters: ""  // Empty string - ORDER BY columns/directions can't be parameterized
})`,
        },
    },
    // OracleDB < 0.0.3 uses legacy format
    {
        pluginId: "oracledb",
        minVersion: "0.0.0",
        definition: {
            className: "OracleDB",
            pluginId: "oracledb",
            description: "Oracle Database integration (legacy)",
            typeDefinition: `export class OracleDB extends Integration {
  constructor(
    name: string,
    integrationId: string,
    config: {
      statement: Binding<string>;
    }
  );

  /** Execute SQL statement - returns array of rows */
  async execute(): Promise<any[]>;
}`,
            example: `new OracleDB("GetEmployees", oracledbIntegrationId, {
  statement: ({ deptId }) =>
    \`SELECT * FROM employees WHERE department_id = \${deptId}\`
})`,
        },
    },
];
/**
 * Get a version-specific type definition if one exists for the given plugin and version.
 */
function getVersionSpecificDefinition(pluginId, version) {
    if (!version) {
        return undefined;
    }
    // Find the first matching version-specific definition
    // Definitions are ordered by version (highest first), so we find the first one
    // where the current version is >= the minVersion
    for (const versionDef of VERSION_SPECIFIC_TYPE_DEFINITIONS) {
        if (versionDef.pluginId === pluginId) {
            if (semverGte(version, versionDef.minVersion)) {
                return versionDef.definition;
            }
        }
    }
    return undefined;
}
/**
 * Create a type definition for a derived plugin.
 * This template is used for all integrations that use a derived plugin (eg extends OpenApi).
 */
export function createExtendedPluginTypeDefinition(plugin, basePluginId, description = `${plugin.id} API integration`) {
    const predefined = INTEGRATION_TYPE_DEFINITIONS[basePluginId];
    if (!predefined)
        return undefined;
    // Replace "BaseClass extends ParentClass" with "DerivedClass extends BaseClass"
    const regex = new RegExp(`\\b${predefined.className}\\s+extends\\s+\\w+`, "g");
    const className = pluginIdToSdkClassName(plugin.id);
    const updatedTypeDef = predefined.typeDefinition.replace(regex, `${className} extends ${predefined.className}`);
    const updatedExample = predefined.example
        ? predefined.example.replace(new RegExp(`new\\s+${predefined.className}\\b`, "g"), `new ${className}`)
        : undefined;
    return {
        className,
        pluginId: plugin.id,
        description,
        typeDefinition: updatedTypeDef,
        example: updatedExample,
    };
}
/**
 * Get type definition for a specific integration by its plugin ID.
 * For OpenAPI-derived plugins not in the predefined list, generates a definition using the template.
 *
 * @param plugin - The plugin header containing plugin metadata
 * @param version - Optional plugin execution version to get version-specific definitions
 * @param options - Optional configuration including feature flags
 */
export function getIntegrationTypeDefinition(plugin, version, options) {
    // If parameterized APIs feature is not explicitly enabled, force version to "0.0.0"
    // to get legacy definitions (without parameters field) for SQL plugins.
    // This matches the UI behavior where ldFlag defaults to false when missing.
    let effectiveVersion = version;
    if (options?.parameterizedApisEnabled !== true) {
        effectiveVersion = "0.0.0";
    }
    // First, check for version-specific definitions
    const versionSpecific = getVersionSpecificDefinition(plugin.id, effectiveVersion);
    if (versionSpecific) {
        return versionSpecific;
    }
    // Fall back to predefined integration
    const predefined = INTEGRATION_TYPE_DEFINITIONS[plugin.id];
    if (predefined) {
        return predefined;
    }
    // For unknown plugins, check if they might be OpenAPI-based
    // You can add logic here to detect OpenAPI-based plugins and generate definitions dynamically
    // For example:
    const basePluginId = getBasePluginId(plugin.id);
    if (basePluginId == plugin.id) {
        return undefined;
    }
    return createExtendedPluginTypeDefinition(plugin, basePluginId);
}
/**
 * Get type definitions for multiple integrations
 */
export function getIntegrationTypeDefinitions(pluginIds) {
    const result = {};
    for (const pluginId of pluginIds) {
        const def = INTEGRATION_TYPE_DEFINITIONS[pluginId];
        if (def) {
            result[pluginId] = def;
        }
    }
    return result;
}
function pluginIdToSdkClassName(id) {
    return datasourceSdkClassByType[id];
}
//# sourceMappingURL=integration-types.js.map