import { ServiceBubble } from '../../../types/service-bubble-class.js';
import { CredentialType } from '@bubblelab/shared-schemas';
import { SalesforceParamsSchema, SalesforceResultSchema, } from './salesforce.schema.js';
import { buildSoqlQuery, buildApiUrl, enhanceSalesforceErrorMessage, parseSalesforceError, } from './salesforce.utils.js';
/**
 * Salesforce Service Bubble
 *
 * Comprehensive Salesforce CRM integration using raw REST API calls.
 * Supports OAuth 2.0 Web Server Flow authentication.
 *
 * Features:
 * - SOQL queries for flexible data retrieval
 * - CRUD operations on any sObject (Account, Contact, Lead, Opportunity, etc.)
 * - Metadata operations (describe sObjects, list available objects)
 * - SOSL search across multiple objects
 * - User info retrieval
 *
 * Use cases:
 * - Sync customer data between systems
 * - Automate lead creation and management
 * - Query and report on sales data
 * - Update opportunity stages and amounts
 * - Create contacts from form submissions
 *
 * Authentication:
 * - OAuth 2.0 Web Server Flow
 * - Automatic token refresh handling
 * - Instance URL management
 */
export class SalesforceBubble extends ServiceBubble {
    static type = 'service';
    static service = 'salesforce';
    static authType = 'oauth';
    static bubbleName = 'salesforce';
    static schema = SalesforceParamsSchema;
    static resultSchema = SalesforceResultSchema;
    static shortDescription = 'Salesforce CRM integration for managing accounts, contacts, leads, and opportunities';
    static longDescription = `
    Salesforce CRM service integration for comprehensive customer relationship management.
    Uses raw REST API calls with OAuth 2.0 authentication.

    Features:
    - SOQL queries for flexible data retrieval
    - CRUD operations on any sObject (Account, Contact, Lead, Opportunity, etc.)
    - Metadata operations (describe sObjects, list available objects)
    - SOSL search across multiple objects
    - User info retrieval

    Use cases:
    - Sync customer data between systems
    - Automate lead creation and management
    - Query and report on sales data
    - Update opportunity stages and amounts
    - Create contacts from form submissions

    Authentication:
    - OAuth 2.0 Web Server Flow
    - Automatic token refresh handling
  `;
    static alias = 'crm';
    constructor(params = {
        operation: 'list_accounts',
        limit: 10,
    }, context) {
        super(params, context);
    }
    async testCredential() {
        const oauthData = this.getOAuthData();
        if (!oauthData) {
            return false;
        }
        try {
            // Test by fetching user info (minimal API call)
            const response = await this.makeSalesforceRequest('/services/oauth2/userinfo', 'GET');
            return response !== null && typeof response === 'object';
        }
        catch {
            return false;
        }
    }
    /**
     * Get OAuth data from credential
     */
    getOAuthData() {
        const credential = this.chooseCredential();
        if (!credential) {
            return undefined;
        }
        // The credential value contains JSON with OAuth data
        try {
            const parsed = JSON.parse(credential);
            if (parsed.access_token && parsed.instance_url) {
                return parsed;
            }
        }
        catch {
            // Credential might just be an access token for simple setups
            // This won't work without instance_url though
        }
        return undefined;
    }
    async makeSalesforceRequest(endpoint, method = 'GET', body) {
        const oauthData = this.getOAuthData();
        if (!oauthData) {
            throw new Error('Salesforce OAuth credentials are required. Please connect your Salesforce account.');
        }
        const url = buildApiUrl(oauthData.instance_url, endpoint);
        const headers = {
            Authorization: `Bearer ${oauthData.access_token}`,
            'Content-Type': 'application/json',
        };
        const requestInit = {
            method,
            headers,
        };
        if (body && method !== 'GET') {
            requestInit.body = JSON.stringify(body);
        }
        const response = await fetch(url, requestInit);
        // Handle 204 No Content (successful DELETE, etc.)
        if (response.status === 204) {
            return { success: true };
        }
        const responseBody = await response.json().catch(() => null);
        if (!response.ok) {
            const errors = parseSalesforceError(responseBody);
            const errorMessage = enhanceSalesforceErrorMessage(errors, response.status);
            throw new Error(errorMessage);
        }
        return responseBody;
    }
    async performAction(context) {
        void context;
        const { operation } = this.params;
        try {
            const result = await (async () => {
                const parsedParams = this.params;
                switch (operation) {
                    case 'query':
                        return await this.executeQuery(parsedParams);
                    case 'get_user_info':
                        return await this.getUserInfo();
                    case 'describe_sobject':
                        return await this.describeSObject(parsedParams);
                    case 'list_sobjects':
                        return await this.listSObjects();
                    case 'get_record':
                        return await this.getRecord(parsedParams);
                    case 'create_record':
                        return await this.createRecord(parsedParams);
                    case 'update_record':
                        return await this.updateRecord(parsedParams);
                    case 'delete_record':
                        return await this.deleteRecord(parsedParams);
                    case 'search':
                        return await this.executeSearch(parsedParams);
                    case 'create_account':
                        return await this.createAccount(parsedParams);
                    case 'create_contact':
                        return await this.createContact(parsedParams);
                    case 'create_lead':
                        return await this.createLead(parsedParams);
                    case 'create_opportunity':
                        return await this.createOpportunity(parsedParams);
                    case 'list_accounts':
                        return await this.listAccounts(parsedParams);
                    case 'list_contacts':
                        return await this.listContacts(parsedParams);
                    case 'list_leads':
                        return await this.listLeads(parsedParams);
                    case 'list_opportunities':
                        return await this.listOpportunities(parsedParams);
                    default:
                        throw new Error(`Unsupported operation: ${operation}`);
                }
            })();
            return result;
        }
        catch (error) {
            return {
                operation,
                success: false,
                error: error instanceof Error ? error.message : 'Unknown error occurred',
            };
        }
    }
    // ============================================================================
    // QUERY OPERATIONS
    // ============================================================================
    async executeQuery(params) {
        const { query } = params;
        const encodedQuery = encodeURIComponent(query);
        const response = await this.makeSalesforceRequest(`/query?q=${encodedQuery}`, 'GET');
        const result = response;
        return {
            operation: 'query',
            success: true,
            result: {
                totalSize: result.totalSize,
                done: result.done,
                nextRecordsUrl: result.nextRecordsUrl,
                records: result.records,
            },
            error: '',
        };
    }
    async executeSearch(params) {
        const { search_query } = params;
        const encodedQuery = encodeURIComponent(search_query);
        const response = await this.makeSalesforceRequest(`/search?q=${encodedQuery}`, 'GET');
        const result = response;
        return {
            operation: 'search',
            success: true,
            searchRecords: result.searchRecords,
            error: '',
        };
    }
    // ============================================================================
    // USER INFO OPERATIONS
    // ============================================================================
    async getUserInfo() {
        const response = await this.makeSalesforceRequest('/services/oauth2/userinfo', 'GET');
        const userInfo = response;
        return {
            operation: 'get_user_info',
            success: true,
            user_info: {
                user_id: userInfo.user_id,
                organization_id: userInfo.organization_id,
                username: userInfo.preferred_username,
                display_name: userInfo.name,
                email: userInfo.email,
                active: userInfo.active,
                user_type: userInfo.user_type,
            },
            error: '',
        };
    }
    // ============================================================================
    // METADATA OPERATIONS
    // ============================================================================
    async describeSObject(params) {
        const { sobject_type } = params;
        const response = await this.makeSalesforceRequest(`/sobjects/${sobject_type}/describe`, 'GET');
        const metadata = response;
        return {
            operation: 'describe_sobject',
            success: true,
            metadata: {
                name: metadata.name,
                label: metadata.label,
                labelPlural: metadata.labelPlural,
                fields: metadata.fields.map((f) => ({
                    name: f.name,
                    label: f.label,
                    type: f.type,
                    required: !f.nillable,
                    createable: f.createable,
                    updateable: f.updateable,
                    picklistValues: f.picklistValues,
                })),
            },
            error: '',
        };
    }
    async listSObjects() {
        const response = await this.makeSalesforceRequest('/sobjects', 'GET');
        const data = response;
        return {
            operation: 'list_sobjects',
            success: true,
            sobjects: data.sobjects.map((s) => ({
                name: s.name,
                label: s.label,
                queryable: s.queryable,
                createable: s.createable,
                updateable: s.updateable,
                deletable: s.deletable,
            })),
            error: '',
        };
    }
    // ============================================================================
    // GENERIC RECORD OPERATIONS
    // ============================================================================
    async getRecord(params) {
        const { sobject_type, record_id, fields } = params;
        let endpoint = `/sobjects/${sobject_type}/${record_id}`;
        if (fields && fields.length > 0) {
            endpoint += `?fields=${fields.join(',')}`;
        }
        const response = await this.makeSalesforceRequest(endpoint, 'GET');
        return {
            operation: 'get_record',
            success: true,
            record: response,
            error: '',
        };
    }
    async createRecord(params) {
        const { sobject_type, data } = params;
        const response = await this.makeSalesforceRequest(`/sobjects/${sobject_type}`, 'POST', data);
        const result = response;
        if (!result.success) {
            return {
                operation: 'create_record',
                success: false,
                errors: result.errors.map((e) => e.message),
                error: result.errors.map((e) => e.message).join('; '),
            };
        }
        return {
            operation: 'create_record',
            success: true,
            id: result.id,
            error: '',
        };
    }
    async updateRecord(params) {
        const { sobject_type, record_id, data } = params;
        await this.makeSalesforceRequest(`/sobjects/${sobject_type}/${record_id}`, 'PATCH', data);
        return {
            operation: 'update_record',
            success: true,
            error: '',
        };
    }
    async deleteRecord(params) {
        const { sobject_type, record_id } = params;
        await this.makeSalesforceRequest(`/sobjects/${sobject_type}/${record_id}`, 'DELETE');
        return {
            operation: 'delete_record',
            success: true,
            error: '',
        };
    }
    // ============================================================================
    // ACCOUNT OPERATIONS
    // ============================================================================
    async createAccount(params) {
        const { name, type, industry, website, phone, billing_city, billing_state, billing_country, additional_fields, } = params;
        const data = { Name: name };
        if (type)
            data.Type = type;
        if (industry)
            data.Industry = industry;
        if (website)
            data.Website = website;
        if (phone)
            data.Phone = phone;
        if (billing_city)
            data.BillingCity = billing_city;
        if (billing_state)
            data.BillingState = billing_state;
        if (billing_country)
            data.BillingCountry = billing_country;
        if (additional_fields)
            Object.assign(data, additional_fields);
        const response = await this.makeSalesforceRequest('/sobjects/Account', 'POST', data);
        const result = response;
        // Fetch the created record to return full data
        const accountResponse = await this.makeSalesforceRequest(`/sobjects/Account/${result.id}`, 'GET');
        return {
            operation: 'create_account',
            success: true,
            account: accountResponse,
            error: '',
        };
    }
    async listAccounts(params) {
        const { limit, fields, where_clause, order_by } = params;
        const defaultFields = [
            'Id',
            'Name',
            'Type',
            'Industry',
            'Website',
            'Phone',
            'BillingCity',
            'BillingState',
            'BillingCountry',
            'CreatedDate',
            'LastModifiedDate',
        ];
        const query = buildSoqlQuery('Account', fields || defaultFields, {
            where: where_clause,
            orderBy: order_by,
            limit,
        });
        const encodedQuery = encodeURIComponent(query);
        const response = await this.makeSalesforceRequest(`/query?q=${encodedQuery}`, 'GET');
        const result = response;
        return {
            operation: 'list_accounts',
            success: true,
            accounts: result.records,
            total_size: result.totalSize,
            error: '',
        };
    }
    // ============================================================================
    // CONTACT OPERATIONS
    // ============================================================================
    async createContact(params) {
        const { first_name, last_name, email, phone, title, account_id, additional_fields, } = params;
        const data = { LastName: last_name };
        if (first_name)
            data.FirstName = first_name;
        if (email)
            data.Email = email;
        if (phone)
            data.Phone = phone;
        if (title)
            data.Title = title;
        if (account_id)
            data.AccountId = account_id;
        if (additional_fields)
            Object.assign(data, additional_fields);
        const response = await this.makeSalesforceRequest('/sobjects/Contact', 'POST', data);
        const result = response;
        // Fetch the created record to return full data
        const contactResponse = await this.makeSalesforceRequest(`/sobjects/Contact/${result.id}`, 'GET');
        return {
            operation: 'create_contact',
            success: true,
            contact: contactResponse,
            error: '',
        };
    }
    async listContacts(params) {
        const { limit, fields, where_clause, order_by, account_id } = params;
        const defaultFields = [
            'Id',
            'FirstName',
            'LastName',
            'Email',
            'Phone',
            'Title',
            'AccountId',
            'CreatedDate',
            'LastModifiedDate',
        ];
        let whereClause = where_clause;
        if (account_id) {
            whereClause = whereClause
                ? `${whereClause} AND AccountId = '${account_id}'`
                : `AccountId = '${account_id}'`;
        }
        const query = buildSoqlQuery('Contact', fields || defaultFields, {
            where: whereClause,
            orderBy: order_by,
            limit,
        });
        const encodedQuery = encodeURIComponent(query);
        const response = await this.makeSalesforceRequest(`/query?q=${encodedQuery}`, 'GET');
        const result = response;
        return {
            operation: 'list_contacts',
            success: true,
            contacts: result.records,
            total_size: result.totalSize,
            error: '',
        };
    }
    // ============================================================================
    // LEAD OPERATIONS
    // ============================================================================
    async createLead(params) {
        const { first_name, last_name, company, email, phone, title, status, lead_source, additional_fields, } = params;
        const data = {
            LastName: last_name,
            Company: company,
        };
        if (first_name)
            data.FirstName = first_name;
        if (email)
            data.Email = email;
        if (phone)
            data.Phone = phone;
        if (title)
            data.Title = title;
        if (status)
            data.Status = status;
        if (lead_source)
            data.LeadSource = lead_source;
        if (additional_fields)
            Object.assign(data, additional_fields);
        const response = await this.makeSalesforceRequest('/sobjects/Lead', 'POST', data);
        const result = response;
        // Fetch the created record to return full data
        const leadResponse = await this.makeSalesforceRequest(`/sobjects/Lead/${result.id}`, 'GET');
        return {
            operation: 'create_lead',
            success: true,
            lead: leadResponse,
            error: '',
        };
    }
    async listLeads(params) {
        const { limit, fields, where_clause, order_by, status } = params;
        const defaultFields = [
            'Id',
            'FirstName',
            'LastName',
            'Email',
            'Phone',
            'Company',
            'Title',
            'Status',
            'LeadSource',
            'CreatedDate',
            'LastModifiedDate',
        ];
        let whereClause = where_clause;
        if (status) {
            whereClause = whereClause
                ? `${whereClause} AND Status = '${status}'`
                : `Status = '${status}'`;
        }
        const query = buildSoqlQuery('Lead', fields || defaultFields, {
            where: whereClause,
            orderBy: order_by,
            limit,
        });
        const encodedQuery = encodeURIComponent(query);
        const response = await this.makeSalesforceRequest(`/query?q=${encodedQuery}`, 'GET');
        const result = response;
        return {
            operation: 'list_leads',
            success: true,
            leads: result.records,
            total_size: result.totalSize,
            error: '',
        };
    }
    // ============================================================================
    // OPPORTUNITY OPERATIONS
    // ============================================================================
    async createOpportunity(params) {
        const { name, stage_name, close_date, amount, probability, account_id, additional_fields, } = params;
        const data = {
            Name: name,
            StageName: stage_name,
            CloseDate: close_date,
        };
        if (amount !== undefined)
            data.Amount = amount;
        if (probability !== undefined)
            data.Probability = probability;
        if (account_id)
            data.AccountId = account_id;
        if (additional_fields)
            Object.assign(data, additional_fields);
        const response = await this.makeSalesforceRequest('/sobjects/Opportunity', 'POST', data);
        const result = response;
        // Fetch the created record to return full data
        const opportunityResponse = await this.makeSalesforceRequest(`/sobjects/Opportunity/${result.id}`, 'GET');
        return {
            operation: 'create_opportunity',
            success: true,
            opportunity: opportunityResponse,
            error: '',
        };
    }
    async listOpportunities(params) {
        const { limit, fields, where_clause, order_by, account_id, stage_name } = params;
        const defaultFields = [
            'Id',
            'Name',
            'StageName',
            'Amount',
            'CloseDate',
            'Probability',
            'AccountId',
            'CreatedDate',
            'LastModifiedDate',
        ];
        let whereClause = where_clause;
        if (account_id) {
            whereClause = whereClause
                ? `${whereClause} AND AccountId = '${account_id}'`
                : `AccountId = '${account_id}'`;
        }
        if (stage_name) {
            whereClause = whereClause
                ? `${whereClause} AND StageName = '${stage_name}'`
                : `StageName = '${stage_name}'`;
        }
        const query = buildSoqlQuery('Opportunity', fields || defaultFields, {
            where: whereClause,
            orderBy: order_by,
            limit,
        });
        const encodedQuery = encodeURIComponent(query);
        const response = await this.makeSalesforceRequest(`/query?q=${encodedQuery}`, 'GET');
        const result = response;
        return {
            operation: 'list_opportunities',
            success: true,
            opportunities: result.records,
            total_size: result.totalSize,
            error: '',
        };
    }
    // ============================================================================
    // CREDENTIAL MANAGEMENT
    // ============================================================================
    chooseCredential() {
        const { credentials } = this.params;
        if (!credentials || typeof credentials !== 'object') {
            return undefined;
        }
        // Salesforce bubble uses SALESFORCE_CRED credentials
        return credentials[CredentialType.SALESFORCE_CRED];
    }
}
//# sourceMappingURL=salesforce.js.map