/**
 * Mailchimp Provider Implementation
 *
 * Implements EmailProviderService using @mailchimp/mailchimp_marketing SDK.
 * Official Node.js SDK for Mailchimp Marketing API v3.
 *
 * Documentation: https://mailchimp.com/developer/marketing/
 * SDK: https://github.com/mailchimp/mailchimp-marketing-node
 */

import MailchimpMarketing from '@mailchimp/mailchimp_marketing';
import type {
  EmailProviderService,
  SendCampaignOptions,
  CampaignResult,
  ContactData,
  ContactResult,
  ListData,
  ListResult,
  SenderData,
  SenderResult,
  CampaignStats,
  WebhookData,
  WebhookResult,
  AccountInfo,
  TemplateResult,
  AutomationWorkflow,
  AutomationContactResult,
} from './EmailProviderService';

export class MailchimpProvider implements EmailProviderService {
  private apiKey: string;
  private dataCenter: string;
  private client: any;

  constructor(apiKey: string) {
    // Extract data center from API key (format: {key}-{dc})
    const parts = apiKey.split('-');
    if (parts.length < 2) {
      throw new Error('Invalid Mailchimp API key format. Expected: {key}-{dataCenter}');
    }

    this.dataCenter = parts.pop() || 'us1';
    this.apiKey = apiKey;

    // Configure Mailchimp client
    this.client = MailchimpMarketing;
    this.client.setConfig({
      apiKey: this.apiKey,
      server: this.dataCenter,
    });
  }

  // ===========================================
  // CONNECTION & ACCOUNT
  // ===========================================

  async verifyConnection(): Promise<boolean> {
    try {
      await this.client.ping.get();
      return true;
    } catch (error) {
      return false;
    }
  }

  async getAccountInfo(): Promise<AccountInfo> {
    const root: any = await this.client.root.getRoot();
    return {
      email: root.email || '',
      firstName: root.account_name || '',
      lastName: '',
      plan: root.pricing_plan?.type || '',
      credits: root.account_name ? undefined : undefined,
    };
  }

  // ===========================================
  // SENDERS
  // Note: Mailchimp uses account-level sender settings, not a separate senders API.
  // We return the account email/name as the default sender.
  // ===========================================

  async getSenders(): Promise<SenderResult[]> {
    const account: any = await this.client.root.getRoot();
    return [{
      id: 1,
      name: account.account_name || '',
      email: account.email || '',
      active: true,
    }];
  }

  async createSender(data: SenderData): Promise<SenderResult> {
    // Mailchimp doesn't support creating senders via API
    // Senders are configured in account settings
    throw new Error('Mailchimp does not support creating senders via API. Configure senders in Mailchimp account settings.');
  }

  // ===========================================
  // LISTS (AUDIENCES)
  // ===========================================

  async getLists(limit: number = 50, offset: number = 0): Promise<ListResult[]> {
    const response: any = await this.client.lists.getAllLists({
      count: limit,
      offset: offset,
    });

    return (response.lists || []).map((list: any) => ({
      id: list.id,
      name: list.name || '',
      totalSubscribers: list.stats?.member_count || 0,
      dynamicList: false,
    }));
  }

  async createList(data: ListData): Promise<ListResult> {
    // Mailchimp requires these fields when creating a list:
    // - name, contact, permission_reminder, campaign_defaults, email_type_option
    const response: any = await this.client.lists.createList({
      name: data.name,
      contact: {
        company: data.company || 'Company',
        address1: data.address || '123 Main St',
        city: data.city || 'New York',
        state: data.state || 'NY',
        zip: data.zip || '10001',
        country: data.country || 'US',
      },
      permission_reminder: 'You are receiving this email because you signed up for our newsletter.',
      campaign_defaults: {
        from_name: data.senderName || 'Newsletter',
        from_email: data.senderEmail || 'newsletter@example.com',
        subject: '',
        language: 'en',
      },
      email_type_option: false,
    });

    return {
      id: response.id,
      name: data.name,
      totalSubscribers: 0,
      dynamicList: false,
    };
  }

  async addContactsToList(listId: number | string, emails: string[]): Promise<void> {
    const members = emails.map(email => ({
      email_address: email,
      status: 'subscribed' as const,
      email_type: 'html' as const,
    }));

    await this.client.lists.batchListMembers(String(listId), {
      members,
      update_existing: true,
    });
  }

  /**
   * Add contacts to a list with merge fields (attributes)
   * For Mailchimp, merge_fields are FNAME, LNAME, PHONE, COMPANY, etc.
   */
  async addContactToListWithData(listId: number | string, contactData: {
    email: string;
    firstName?: string;
    lastName?: string;
    phone?: string;
    company?: string;
    address?: string;
    city?: string;
    state?: string;
    zip?: string;
    country?: string;
    birthday?: string;
  }): Promise<void> {
    // Build merge_fields object for Mailchimp
    const mergeFields: Record<string, string> = {};

    if (contactData.firstName) mergeFields.FNAME = contactData.firstName;
    if (contactData.lastName) mergeFields.LNAME = contactData.lastName;
    if (contactData.phone) mergeFields.PHONE = contactData.phone;
    if (contactData.company) mergeFields.COMPANY = contactData.company;
    if (contactData.address) mergeFields.ADDRESS = contactData.address;
    if (contactData.city) mergeFields.CITY = contactData.city;
    if (contactData.state) mergeFields.STATE = contactData.state;
    if (contactData.zip) mergeFields.ZIP = contactData.zip;
    if (contactData.country) mergeFields.COUNTRY = contactData.country;
    if (contactData.birthday) mergeFields.BIRTHDAY = contactData.birthday;

    const memberData: any = {
      email_address: contactData.email,
      status: 'subscribed' as const,
      email_type: 'html' as const,
    };

    // Only add merge_fields if we have any
    if (Object.keys(mergeFields).length > 0) {
      memberData.merge_fields = mergeFields;
    }

    await this.client.lists.batchListMembers(String(listId), {
      members: [memberData],
      update_existing: true,
    });
  }

  async removeContactsFromList(listId: number | string, emails: string[]): Promise<void> {
    // Mailchimp requires MD5 hash of lowercase email for member operations
    const crypto = await import('crypto');

    for (const email of emails) {
      const subscriberHash = crypto.createHash('md5').update(email.toLowerCase()).digest('hex');
      try {
        await this.client.lists.deleteListMember(String(listId), subscriberHash);
      } catch (error) {
        // Ignore if member doesn't exist
      }
    }
  }

  /**
   * Get all members/contacts from a specific list
   */
  async getListMembers(listId: number | string, limit: number = 100, offset: number = 0): Promise<{
    members: Array<{ id: string | number; email: string; firstName?: string; lastName?: string; status?: string; createdAt?: string; attributes?: Record<string, any> }>;
    total: number;
  }> {
    try {
      const response: any = await this.client.lists.getListMembersInfo(String(listId), {
        count: limit,
        offset: offset,
      });

      const members = (response.members || []).map((member: any) => {
        const mergeFields = member.merge_fields || {};
        return {
          id: member.id || member.email_address,
          email: member.email_address,
          firstName: mergeFields.FNAME || mergeFields.firstName || '',
          lastName: mergeFields.LNAME || mergeFields.lastName || '',
          status: member.status || 'subscribed',
          createdAt: member.timestamp_signup || member.timestamp_opt,
          attributes: mergeFields,
        };
      });

      return {
        members,
        total: response.total_items || members.length,
      };
    } catch (error: any) {
      console.error('[MailchimpProvider] Failed to get list members:', error);
      // Log more details for debugging
      console.error('[MailchimpProvider] Error details:', {
        message: error.message,
        status: error.status,
        response: error.response?.data || error.response?.body,
      });
      throw error;
    }
  }

  // ===========================================
  // CONTACTS (MEMBERS)
  // ===========================================

  async createContact(data: ContactData): Promise<ContactResult> {
    const listId = data.listIds?.[0];
    if (!listId) {
      throw new Error('List ID is required to create a contact in Mailchimp');
    }

    const memberData: any = {
      email_address: data.email,
      status: 'subscribed',
      email_type: 'html',
    };

    if (data.attributes) {
      memberData.merge_fields = data.attributes;
    }

    const response: any = await this.client.lists.addListMember(String(listId), memberData);

    return {
      id: response.id || 0,
      email: data.email,
      created: true,
    };
  }

  async updateContact(email: string, data: Partial<ContactData>): Promise<ContactResult> {
    const crypto = await import('crypto');
    const subscriberHash = crypto.createHash('md5').update(email.toLowerCase()).digest('hex');

    const updateData: any = {};
    if (data.attributes) {
      updateData.merge_fields = data.attributes;
    }

    // If listIds provided, need to add to lists
    // Mailchimp handles list membership differently - contacts belong to one primary list
    const listId = data.listIds?.[0];
    if (listId) {
      const response: any = await this.client.lists.updateListMember(
        String(listId),
        subscriberHash,
        {
          email_address: email,
          status: 'subscribed',
          merge_fields: data.attributes || {},
        }
      );

      return {
        id: response.id || 0,
        email,
        updated: true,
      };
    }

    return {
      id: 0,
      email,
      updated: false,
    };
  }

  async getContact(email: string): Promise<ContactResult | null> {
    // Mailchimp contacts belong to lists, need to search across lists
    try {
      const response: any = await this.client.searchCampaigns.searchMembers(email);
      if (response.exact_matches?.members?.[0]) {
        const member = response.exact_matches.members[0];
        return {
          id: member.id || 0,
          email: member.email_address || email,
          created: false,
        };
      }
      return null;
    } catch {
      return null;
    }
  }

  async deleteContact(email: string): Promise<void> {
    // Delete from all lists (archive)
    const crypto = await import('crypto');
    const subscriberHash = crypto.createHash('md5').update(email.toLowerCase()).digest('hex');

    const lists = await this.getLists();
    for (const list of lists) {
      try {
        await this.client.lists.deleteListMember(String(list.id), subscriberHash);
      } catch {
        // Ignore if not in this list
      }
    }
  }

  async importContacts(contacts: ContactData[]): Promise<{ taskId: string }> {
    // Mailchimp uses batch operations for bulk import
    const listId = contacts[0]?.listIds?.[0];
    if (!listId) {
      throw new Error('List ID is required for bulk import');
    }

    const members = contacts.map(contact => ({
      email_address: contact.email,
      status: 'subscribed' as const,
      email_type: 'html' as const,
      merge_fields: contact.attributes || {},
    }));

    const response: any = await this.client.lists.batchListMembers(String(listId), {
      members,
      update_existing: true,
    });

    return {
      taskId: response.id || 'batch-complete',
    };
  }

  // ===========================================
  // CAMPAIGNS
  // ===========================================

  async createCampaign(options: SendCampaignOptions): Promise<CampaignResult> {
    const campaignData: any = {
      type: 'regular',
      recipients: {
        list_id: String(options.recipients?.listIds?.[0] || ''),
      },
      settings: {
        title: options.name,
        subject_line: options.subject,
        from_name: options.sender.name,
        reply_to: options.sender.email,
      },
    };

    if (options.templateId) {
      campaignData.settings.template_id = options.templateId;
    }

    if (options.tags?.length) {
      campaignData.settings.tags = options.tags;
    }

    const response: any = await this.client.campaigns.create(campaignData);

    // Set content if provided
    if (options.htmlContent) {
      await this.client.campaigns.setContent(String(response.id), {
        html: options.htmlContent,
      });
    }

    return {
      campaignId: response.id,
      status: response.status || 'save',
    };
  }

  async updateCampaign(campaignId: number, options: Partial<SendCampaignOptions>): Promise<CampaignResult> {
    const updateData: any = {};

    if (options.name) updateData.settings = { ...updateData.settings, title: options.name };
    if (options.subject) updateData.settings = { ...updateData.settings, subject_line: options.subject };
    if (options.sender?.email) updateData.settings = { ...updateData.settings, reply_to: options.sender.email };
    if (options.sender?.name) updateData.settings = { ...updateData.settings, from_name: options.sender.name };

    await this.client.campaigns.update(String(campaignId), updateData);

    if (options.htmlContent) {
      await this.client.campaigns.setContent(String(campaignId), {
        html: options.htmlContent,
      });
    }

    return {
      campaignId,
      status: 'save',
    };
  }

  async getCampaign(campaignId: number): Promise<{
    id: number;
    name: string;
    subject: string;
    status: string;
    stats?: CampaignStats;
  }> {
    const campaign: any = await this.client.campaigns.get(String(campaignId));

    return {
      id: campaign.id,
      name: campaign.settings?.title || campaign.settings?.subject_line || '',
      subject: campaign.settings?.subject_line || '',
      status: campaign.status || 'save',
    };
  }

  async sendCampaign(campaignId: number): Promise<void> {
    await this.client.campaigns.send(String(campaignId));
  }

  async scheduleCampaign(campaignId: number, scheduledAt: Date): Promise<void> {
    await this.client.campaigns.schedule(String(campaignId), {
      schedule_time: scheduledAt.toISOString(),
    });
  }

  async sendTestEmail(campaignId: number, email: string): Promise<void> {
    await this.client.campaigns.test(String(campaignId), {
      test_emails: [email],
      send_type: 'html',
    });
  }

  async deleteCampaign(campaignId: number): Promise<void> {
    await this.client.campaigns.remove(String(campaignId));
  }

  // ===========================================
  // TEMPLATES
  // Note: Only Classic templates can be created via API
  // ===========================================

  async getTemplates(): Promise<TemplateResult[]> {
    const response: any = await this.client.templates.list();

    return (response.templates || []).map((template: any) => ({
      id: template.id,
      name: template.name || '',
      subject: '', // Templates don't store subject
      isActive: true,
    }));
  }

  async createTemplate(data: {
    name: string;
    subject: string;
    htmlContent: string;
    sender: { name: string; email: string };
  }): Promise<{ id: number }> {
    const response: any = await this.client.templates.create({
      name: data.name,
      html: data.htmlContent,
    });

    return { id: response.id };
  }

  // ===========================================
  // WEBHOOKS
  // ===========================================

  async createWebhook(_data: WebhookData): Promise<WebhookResult> {
    // Mailchimp webhooks are list-specific
    // We need a list ID, but this method signature doesn't have it
    // Store webhook URL for later use when we have a list
    throw new Error('Mailchimp webhooks require a list ID. Use createWebhookForList method.');
  }

  async createWebhookForList(listId: string, data: WebhookData): Promise<WebhookResult> {
    const response: any = await this.client.lists.createListWebhook(listId, {
      url: data.url,
      events: {
        subscribe: data.events.includes('subscribe'),
        unsubscribe: data.events.includes('unsubscribe'),
        profile: data.events.includes('profile'),
        cleaned: data.events.includes('cleaned'),
        upemail: data.events.includes('upemail'),
        campaign: data.events.includes('campaign'),
      },
      sources: {
        user: true,
        admin: true,
        api: true,
      },
    });

    return {
      id: response.id,
      url: data.url,
      events: data.events,
    };
  }

  async getWebhooks(): Promise<WebhookResult[]> {
    // Mailchimp webhooks are list-specific
    // This returns webhooks for all lists
    const lists = await this.getLists();
    const webhookPromises = lists.map(list =>
      this.client.lists.getListWebhooks(String(list.id))
        .then((response: any) => ({
          listId: list.id,
          webhooks: response.webhooks || [],
        }))
        .catch(() => ({ listId: list.id, webhooks: [] }))
    );

    const results = await Promise.all(webhookPromises);
    const allWebhooks: WebhookResult[] = [];

    for (const result of results) {
      for (const webhook of result.webhooks) {
        allWebhooks.push({
          id: webhook.id,
          url: webhook.url,
          events: Object.keys(webhook.events || {}).filter(key => webhook.events[key]),
        });
      }
    }

    return allWebhooks;
  }

  async deleteWebhook(webhookId: number): Promise<void> {
    // Need list ID to delete webhook
    // This is a limitation - we need to search all lists
    const lists = await this.getLists();

    for (const list of lists) {
      try {
        await this.client.lists.deleteListWebhook(String(list.id), String(webhookId));
        return;
      } catch {
        // Continue to next list
      }
    }
  }

  // ===========================================
  // REPORTS & ANALYTICS
  // ===========================================

  async getCampaignStats(campaignId: number): Promise<CampaignStats> {
    try {
      const report: any = await this.client.reports.getCampaignReport(String(campaignId));

      return {
        sent: report.emails_sent || 0,
        delivered: (report.emails_sent || 0) - (report.bounces?.hard_bounces || 0) - (report.bounces?.soft_bounces || 0),
        opened: report.opens?.opens || 0,
        uniqueOpens: report.opens?.unique_opens || 0,
        clicked: report.clicks?.clicks_total || 0,
        uniqueClicks: report.clicks?.unique_clicks || 0,
        hardBounces: report.bounces?.hard_bounces || 0,
        softBounces: report.bounces?.soft_bounces || 0,
        unsubscribes: report.unsubscribed || 0,
        complaints: report.abuse_reports || 0,
      };
    } catch (error) {
      // Campaign might not have stats yet
      return {
        sent: 0,
        delivered: 0,
        opened: 0,
        uniqueOpens: 0,
        clicked: 0,
        uniqueClicks: 0,
        hardBounces: 0,
        softBounces: 0,
        unsubscribes: 0,
        complaints: 0,
      };
    }
  }

  // ===========================================
  // AUTOMATION WORKFLOWS
  // Note: Mailchimp automations (Customer Journeys) have limited API support
  // ===========================================

  async getAutomations(): Promise<AutomationWorkflow[]> {
    try {
      const response: any = await this.client.automations.list();

      return (response.automations || []).map((automation: any) => ({
        id: automation.id,
        name: automation.settings?.title || automation.id,
        status: automation.status === 'sending' ? 'active' : automation.status === 'paused' ? 'inactive' : 'draft',
        trigger: automation.recipients?.list_id ? {
          type: 'list_subscription',
          description: 'Triggered by list subscription',
        } : undefined,
        totalContacts: automation.recipients?.segment_opts?.match || 0,
      }));
    } catch (error) {
      // Mailchimp Classic Automations API may not be available
      console.error('[MailchimpProvider] Failed to get automations:', error);
      return [];
    }
  }

  async addContactToAutomation(
    _workflowId: number,
    email: string,
    _attributes?: Record<string, any>,
    listIds?: number[]
  ): Promise<AutomationContactResult> {
    // Mailchimp Customer Journeys have limited API support
    // For classic automations, contacts are added via list subscription
    try {
      // If list provided, subscribe the contact
      if (listIds && listIds.length > 0) {
        await this.addContactsToList(listIds[0], [email]);
      }

      return {
        success: true,
        message: 'Contact subscribed to list. Mailchimp automations trigger based on list events.',
      };
    } catch (error: any) {
      return {
        success: false,
        message: error.message || 'Failed to add contact to automation',
      };
    }
  }

  async removeContactFromAutomation(_workflowId: number, _email: string): Promise<void> {
    // Mailchimp automations don't support direct contact removal via API
    throw new Error('Mailchimp does not support removing contacts from automations via API. Use Mailchimp dashboard to manage automation contacts.');
  }

  async getAutomationContacts(
    _workflowId: number,
    _limit?: number,
    _offset?: number
  ): Promise<{ contacts: ContactResult[]; total: number }> {
    // Mailchimp doesn't have a direct API to list automation contacts
    return { contacts: [], total: 0 };
  }

  // ===========================================
  // SEGMENTS & TAGS
  // ===========================================

  async getTags(listId: number): Promise<Array<{ id: number; name: string; count: number }>> {
    const response: any = await this.client.lists.getSegments(String(listId));

    return (response.segments || [])
      .filter((segment: any) => segment.type === 'static')
      .map((tag: any) => ({
        id: tag.id,
        name: tag.name,
        count: tag.member_count || 0,
      }));
  }

  async addTagToContact(listId: number, subscriberHash: string, tags: string[]): Promise<void> {
    await this.client.lists.updateListMemberTags(String(listId), subscriberHash, {
      tags: tags.map(name => ({ name, status: 'active' })),
    });
  }

  async removeTagFromContact(listId: number, subscriberHash: string, tags: string[]): Promise<void> {
    await this.client.lists.updateListMemberTags(String(listId), subscriberHash, {
      tags: tags.map(name => ({ name, status: 'inactive' })),
    });
  }

  // ===========================================
  // HEALTH CHECK
  // ===========================================

  async healthCheck(): Promise<{ status: 'ok' | 'error'; message?: string }> {
    try {
      await this.client.ping.get();
      return { status: 'ok' };
    } catch (error: any) {
      return {
        status: 'error',
        message: error.message || 'Failed to connect to Mailchimp API',
      };
    }
  }

  // ===========================================
  // TRANSACTIONAL EMAIL (for automation workflows)
  // ===========================================

  async sendTransactionalEmail(params: {
    to: string;
    subject: string;
    htmlContent?: string;
    htmlUrl?: string;
    templateId?: number;
    sender: { email: string; name: string; id?: number };
    replyTo?: string;
    tags?: string[];
  }): Promise<{ success: boolean; messageId?: string; campaignId?: string | number; error?: string }> {
    try {
      // Mailchimp doesn't have a transactional email API (Mandrill is separate)
      // Create a single-recipient campaign and send it immediately
      const campaignData: any = {
        type: 'regular',
        recipients: {
          list_id: undefined, // Will set after creating a temporary list or using existing
        },
        settings: {
          subject_line: params.subject,
          from_name: params.sender.name,
          reply_to: params.replyTo || params.sender.email,
          title: `[Automation] ${params.subject}`,
        },
      };

      // Get the first available list to send to
      const lists = await this.getLists(1, 0);
      if (!lists || lists.length === 0) {
        return {
          success: false,
          error: 'No Mailchimp audience/list available. Create an audience first.',
        };
      }

      // Create campaign
      const campaign = await this.client.campaigns.create({
        type: 'regular',
        recipients: { list_id: String(lists[0].id) },
        settings: {
          subject_line: params.subject,
          from_name: params.sender.name,
          reply_to: params.replyTo || params.sender.email,
          title: `[Automation] ${params.subject}`,
        },
      });

      // Set content
      if (params.htmlContent) {
        await this.client.campaigns.setContent(campaign.id, {
          html: params.htmlContent,
        });
      }

      // Send immediately
      await this.client.campaigns.send(campaign.id);

      return {
        success: true,
        messageId: campaign.id?.toString(),
        campaignId: campaign.id,
      };
    } catch (error: any) {
      console.error('[MailchimpProvider] Error sending transactional email:', error);
      return {
        success: false,
        error: error.message || 'Failed to send transactional email via Mailchimp',
      };
    }
  }
}