/**
 * Zoho Campaigns Adapter
 *
 * Implements ProviderAdapter for Zoho Campaigns.
 * Wraps the existing ZohoCampaignsProvider (EmailProviderService) and adds:
 *   - Automatic list ID resolution when not provided
 *   - Retry with exponential backoff
 *   - Error normalization
 *
 * Auth: OAuth 2.0 with access token + refresh token (handled by ZohoCampaignsProvider)
 *
 * Important limitations:
 *   - Zoho Campaigns public API does NOT support workflow triggering.
 *     triggerAutomation returns a descriptive ProviderError.
 *   - Zoho requires content_url for HTML content (no inline HTML in campaigns).
 *   - Sender management is UI-only (no API).
 */

import type { EmailProviderService } from '../../email/EmailProviderService';
import type {
  ProviderAdapter,
  AddContactInput,
  SendCampaignInput,
  TriggerAutomationInput,
  ProviderResult,
  AddContactOutput,
  SendCampaignOutput,
  TriggerAutomationOutput,
  GetStatusOutput,
} from './types';
import { ProviderError } from './errorNormalizer';
import { normalizeProviderError } from './errorNormalizer';
import { withRetry } from './retry';

export class ZohoAdapter implements ProviderAdapter {
  private provider: EmailProviderService;

  constructor(provider: EmailProviderService) {
    this.provider = provider;
  }

  // ===========================================
  // ADD CONTACT
  // ===========================================

  async addContact(input: AddContactInput): Promise<ProviderResult<AddContactOutput>> {
    try {
      // Zoho requires a list context for adding contacts.
      // If listIds provided, use addContactToListWithData.
      // Otherwise, find the first available list.
      let listId: string | number | undefined = input.listIds?.[0];

      if (!listId) {
        const lists = await withRetry(
          () => this.provider.getLists(10, 0),
          'addContact.getLists',
          'zoho'
        );

        if (!lists || lists.length === 0) {
          return {
            success: false,
            error: new ProviderError({
              provider: 'zoho',
              code: 'LIST_NOT_FOUND',
              message: 'No Zoho Campaigns list available. Create a list first.',
              retryable: false,
            }),
          };
        }

        listId = lists[0].id;
      }

      // Use addContactToListWithData for richer data
      await withRetry(
        () => (this.provider as any).addContactToListWithData(listId, {
          email: input.email,
          firstName: input.firstName,
          lastName: input.lastName,
          phone: input.phone,
          company: input.company,
        }),
        'addContact',
        'zoho'
      );

      return {
        success: true,
        data: {
          contactId: 0, // Zoho doesn't return a numeric contact ID from this operation
          email: input.email,
          created: true,
        },
      };
    } catch (error) {
      const providerError = normalizeProviderError(error, 'zoho');

      // Zoho may return specific errors for duplicate contacts
      if (providerError.message.toLowerCase().includes('already exist') ||
          providerError.message.toLowerCase().includes('duplicate')) {
        return {
          success: false,
          error: new ProviderError({
            provider: 'zoho',
            code: 'DUPLICATE_CONTACT',
            message: `Contact ${input.email} already exists`,
            originalError: error,
            retryable: false,
          }),
        };
      }

      return { success: false, error: providerError };
    }
  }

  // ===========================================
  // SEND CAMPAIGN
  // ===========================================

  async sendCampaign(input: SendCampaignInput): Promise<ProviderResult<SendCampaignOutput>> {
    try {
      // Zoho requires a list for campaigns
      let listId: string | number | undefined = input.listIds?.[0];

      if (!listId) {
        const lists = await withRetry(
          () => this.provider.getLists(10, 0),
          'sendCampaign.getLists',
          'zoho'
        );

        if (!lists || lists.length === 0) {
          return {
            success: false,
            error: new ProviderError({
              provider: 'zoho',
              code: 'LIST_NOT_FOUND',
              message: 'No Zoho Campaigns list available for campaign.',
              retryable: false,
            }),
          };
        }

        listId = lists[0].id;
      }

      // Step 1: Create the campaign
      // Note: Zoho prefers content_url over inline HTML. If htmlContent is
      // provided, we pass it anyway — the ZohoCampaignsProvider handles this.
      const campaignOptions: any = {
        name: input.name,
        subject: input.subject,
        sender: {
          email: input.sender.email,
          name: input.sender.name,
        },
        recipients: {
          listIds: [listId],
        },
      };

      if (input.htmlContent) campaignOptions.htmlContent = input.htmlContent;
      if (input.htmlUrl) campaignOptions.htmlUrl = input.htmlUrl;
      if (input.replyTo) campaignOptions.replyTo = input.replyTo;

      const createResult = await withRetry(
        () => this.provider.createCampaign(campaignOptions),
        'sendCampaign.create',
        'zoho'
      );

      // Step 2: Send the campaign
      if (!input.scheduledAt) {
        await withRetry(
          () => this.provider.sendCampaign(createResult.campaignId),
          'sendCampaign.send',
          'zoho'
        );
      } else {
        await withRetry(
          () => this.provider.scheduleCampaign(createResult.campaignId, input.scheduledAt!),
          'sendCampaign.schedule',
          'zoho'
        );
      }

      return {
        success: true,
        data: {
          campaignId: createResult.campaignId,
          status: input.scheduledAt ? 'scheduled' : 'sent',
        },
      };
    } catch (error) {
      return { success: false, error: normalizeProviderError(error, 'zoho') };
    }
  }

  // ===========================================
  // TRIGGER AUTOMATION
  // ===========================================

  async triggerAutomation(_input: TriggerAutomationInput): Promise<ProviderResult<TriggerAutomationOutput>> {
    // Zoho Campaigns public API does not support workflow triggering.
    // This is a known limitation. The ZohoCampaignsProvider's
    // addContactToAutomation method also returns { success: false, message }
    // for this reason.
    //
    // Possible future workarounds:
    //   1. Use Zoho's internal/workaround APIs (not documented)
    //   2. Subscribe contacts to a list that feeds into a Zoho automation
    //   3. Use Zoho Flow or webhooks as an intermediary

    return {
      success: false,
      error: new ProviderError({
        provider: 'zoho',
        code: 'PROVIDER_ERROR',
        message: 'Zoho Campaigns public API does not support workflow triggering directly. ' +
          'Use list subscription + Zoho automation rules, or trigger via webhook.',
        retryable: false,
      }),
    };
  }

  // ===========================================
  // GET STATUS
  // ===========================================

  async getStatus(): Promise<ProviderResult<GetStatusOutput>> {
    try {
      // Zoho doesn't have a dedicated account info endpoint.
      // Use healthCheck as the connection test.
      const health = await this.provider.healthCheck();

      if (health.status === 'ok') {
        // Try to get account info (may return empty object for Zoho)
        try {
          const accountInfo = await this.provider.getAccountInfo();
          return {
            success: true,
            data: {
              connected: true,
              provider: 'zoho',
              accountEmail: accountInfo.email || undefined,
              accountName: accountInfo.firstName || undefined,
            },
          };
        } catch {
          // Account info is optional for Zoho
          return {
            success: true,
            data: {
              connected: true,
              provider: 'zoho',
            },
          };
        }
      }

      return {
        success: true,
        data: {
          connected: false,
          provider: 'zoho',
        },
      };
    } catch (error) {
      return { success: false, error: normalizeProviderError(error, 'zoho') };
    }
  }
}