import type { gmail_v1 } from '@googleapis/gmail'
import type { BaseIntegrationAdapter } from '../../../base-integration-adapter'
import { ToolingError } from '../../../../types'
import type { ExecutionContext } from '../../../../../base/types'

/**
 * Gmail credentials format
 * Stored in credentials table, encrypted
 */
interface GmailCredentials {
  type: 'oauth2' | 'service-account'
  // OAuth2 credentials
  clientId?: string
  clientSecret?: string
  refreshToken?: string
  // Service account credentials
  serviceAccountKey?: Record<string, unknown>
}

/**
 * Send email parameters
 */
interface SendEmailParams {
  to: string
  subject: string
  body: string
  cc?: string
  from?: string
  /**
   * Idempotency key. When provided, embedded as the RFC 2822 Message-ID header so
   * duplicate sends from platform_internal retries are deduped on the recipient side.
   * Pass a stable workflow-derived hash (e.g. `<execId>.<step>@elevasis.io`).
   */
  idempotencyKey?: string
}

/**
 * Send email result
 */
interface SendEmailResult {
  messageId: string
  threadId: string
}

/**
 * Gmail adapter for sending emails via Gmail API
 *
 * Supported methods:
 * - sendEmail: Send email message
 *
 * @example
 * const adapter = new GmailAdapter()
 * const result = await adapter.call('sendEmail', {
 *   to: 'user@example.com',
 *   subject: 'Hello',
 *   body: 'This is a test email'
 * }, {
 *   type: 'oauth2',
 *   clientId: '...',
 *   clientSecret: '...',
 *   refreshToken: '...'
 * })
 */
export class GmailAdapter implements BaseIntegrationAdapter {
  readonly name = 'gmail'

  /**
   * Call Gmail API method
   * @param method - Method name (e.g., 'sendEmail')
   * @param params - Method parameters
   * @param credentials - OAuth2 or service account credentials
   * @param context - Execution context for logging and tracing
   */
  async call(
    method: string,
    params: unknown,
    credentials: Record<string, unknown>,
    context?: ExecutionContext
  ): Promise<unknown> {
    // Validate credentials format
    if (!this.validateCredentials(credentials)) {
      throw new ToolingError('credentials_invalid', 'Invalid Gmail credentials', { integration: 'gmail', method })
    }
    const gmailCreds = credentials as unknown as GmailCredentials

    // Create authenticated Gmail client
    const gmail = await this.createClient(gmailCreds)

    // Route to method handler
    switch (method) {
      case 'sendEmail':
        return this.sendEmail(gmail, params as SendEmailParams, context)
      default:
        throw new ToolingError('method_not_found', `Unknown method: ${method}`, { integration: 'gmail', method })
    }
  }

  /**
   * Create authenticated Gmail client (lazy loads @googleapis/gmail SDK)
   */
  private async createClient(creds: GmailCredentials): Promise<gmail_v1.Gmail> {
    const { gmail } = await import('@googleapis/gmail')
    const { OAuth2Client, GoogleAuth } = await import('googleapis-common')

    if (creds.type === 'oauth2') {
      const auth = new OAuth2Client(creds.clientId, creds.clientSecret)
      auth.setCredentials({ refresh_token: creds.refreshToken })

      return gmail({ version: 'v1', auth })
    } else {
      // Service account (for workspace-wide access)
      const auth = new GoogleAuth({
        credentials: creds.serviceAccountKey,
        scopes: ['https://www.googleapis.com/auth/gmail.send']
      })

      return gmail({ version: 'v1', auth: (await auth.getClient()) as any })
    }
  }

  /**
   * Send email via Gmail API
   */
  private async sendEmail(
    gmail: gmail_v1.Gmail,
    params: SendEmailParams,
    context?: ExecutionContext
  ): Promise<SendEmailResult> {
    // Validate params
    if (!params.to || !params.subject || !params.body) {
      throw new ToolingError('validation_error', 'Missing required fields: to, subject, body', { params })
    }

    // Build RFC 2822 formatted message. When idempotencyKey is provided, emit a stable
    // Message-ID header — duplicate sends from platform_internal retries land with the
    // same Message-ID and are deduped by recipient MTAs / threading clients.
    const messageIdHeader = params.idempotencyKey ? `Message-ID: <${params.idempotencyKey}@elevasis.io>` : ''
    const messageParts = [
      `To: ${params.to}`,
      `Subject: ${params.subject}`,
      params.cc ? `Cc: ${params.cc}` : '',
      messageIdHeader,
      'Content-Type: text/html; charset=utf-8',
      '',
      params.body
    ]
    const message = messageParts.filter(Boolean).join('\n')

    // Base64 encode (URL-safe)
    const encodedMessage = Buffer.from(message)
      .toString('base64')
      .replace(/\+/g, '-')
      .replace(/\//g, '_')
      .replace(/=+$/, '')

    try {
      const response = await gmail.users.messages.send({
        userId: 'me',
        requestBody: {
          raw: encodedMessage
        }
      })

      if (context?.logger) {
        context.logger.info(
          `[GmailAdapter] Email sent: organizationId=${context.organizationId} executionId=${context.executionId} messageId=${response.data.id} threadId=${response.data.threadId} to=${params.to}`
        )
      }

      return {
        messageId: response.data.id!,
        threadId: response.data.threadId!
      }
    } catch (error: any) {
      throw new ToolingError('api_error', `Gmail API error: ${error.message}`, {
        statusCode: error.code,
        details: error
      })
    }
  }

  /**
   * Validate credentials structure
   * Required by BaseIntegrationAdapter interface
   */
  validateCredentials(creds: Record<string, unknown>): boolean {
    if (!creds.type) {
      return false
    }

    if (creds.type === 'oauth2') {
      return !!(creds.clientId && creds.clientSecret && creds.refreshToken)
    } else if (creds.type === 'service-account') {
      return !!creds.serviceAccountKey
    }

    return false
  }
}
