import { env } from '../config/env';
import { logger } from '../utils/logger';
import { listmonkClient } from './listmonk.client';
import { listmonkSyncService, SUPPORT_LEVEL_LIST_MAP } from './listmonk-sync.service';

/**
 * Event-driven Listmonk sync — fire-and-forget subscriber upserts
 * triggered by application events (shift signups, canvass completions, campaign emails).
 *
 * All methods silently fail if LISTMONK_SYNC_ENABLED is false or Listmonk is unreachable.
 */
class ListmonkEventSyncService {
  private _lastSyncAt: Date | null = null;
  private _todaySyncCount = 0;
  private _todayDate = '';

  private get enabled(): boolean {
    return env.LISTMONK_SYNC_ENABLED === 'true';
  }

  private incrementCounter(): void {
    const today = new Date().toISOString().split('T')[0];
    if (today !== this._todayDate) {
      this._todayDate = today;
      this._todaySyncCount = 0;
    }
    this._todaySyncCount++;
    this._lastSyncAt = new Date();
  }

  /**
   * Sync a shift signup to Listmonk "All Contacts" + "Volunteers" lists.
   */
  async onShiftSignup(data: {
    email: string;
    name: string;
    shiftTitle: string;
    shiftDate: string;
    cutName?: string;
  }): Promise<void> {
    if (!this.enabled) return;

    try {
      await listmonkSyncService.ensureInitialized();
      const allContactsId = listmonkSyncService.getListId('All Contacts');
      const volunteersId = listmonkSyncService.getListId('Volunteers');
      if (!allContactsId || !volunteersId) return;

      await listmonkClient.upsertSubscriber(
        data.email,
        data.name,
        [allContactsId, volunteersId],
        {
          source: 'shift_signup',
          last_shift_title: data.shiftTitle,
          last_shift_date: data.shiftDate,
          cut_name: data.cutName || null,
          last_synced: new Date().toISOString(),
        },
      );
      this.incrementCounter();
      logger.debug(`Listmonk event sync: shift signup for ${data.email}`);
    } catch (err) {
      logger.debug('Listmonk event sync failed (onShiftSignup):', err);
    }
  }

  /**
   * Sync a completed canvass session to Listmonk "All Contacts" + "Canvassers" lists.
   */
  async onCanvassSessionCompleted(data: {
    email: string;
    name: string;
    cutName: string;
    visitCount: number;
    outcomes: Record<string, number>;
  }): Promise<void> {
    if (!this.enabled) return;

    try {
      await listmonkSyncService.ensureInitialized();
      const allContactsId = listmonkSyncService.getListId('All Contacts');
      const canvassersId = listmonkSyncService.getListId('Canvassers');
      if (!allContactsId || !canvassersId) return;

      await listmonkClient.upsertSubscriber(
        data.email,
        data.name,
        [allContactsId, canvassersId],
        {
          source: 'canvasser',
          last_cut: data.cutName,
          last_visit_count: data.visitCount,
          last_outcomes: data.outcomes,
          last_synced: new Date().toISOString(),
        },
      );
      this.incrementCounter();
      logger.debug(`Listmonk event sync: canvass session for ${data.email}`);
    } catch (err) {
      logger.debug('Listmonk event sync failed (onCanvassSessionCompleted):', err);
    }
  }

  /**
   * Sync a sent campaign email to Listmonk "All Contacts" + "Campaign Participants" lists.
   */
  async onCampaignEmailSent(data: {
    email: string;
    name: string;
    campaignSlug: string;
    postalCode?: string;
  }): Promise<void> {
    if (!this.enabled) return;

    try {
      await listmonkSyncService.ensureInitialized();
      const allContactsId = listmonkSyncService.getListId('All Contacts');
      const participantsId = listmonkSyncService.getListId('Campaign Participants');
      if (!allContactsId || !participantsId) return;

      await listmonkClient.upsertSubscriber(
        data.email,
        data.name,
        [allContactsId, participantsId],
        {
          source: 'campaign_participant',
          campaign_slug: data.campaignSlug,
          postal_code: data.postalCode || null,
          last_synced: new Date().toISOString(),
        },
      );
      this.incrementCounter();
      logger.debug(`Listmonk event sync: campaign email for ${data.email}`);
    } catch (err) {
      logger.debug('Listmonk event sync failed (onCampaignEmailSent):', err);
    }
  }

  /**
   * Sync an activated subscription to Listmonk "All Contacts" + "Subscribers" lists.
   */
  async onSubscriptionActivated(data: {
    email: string;
    name: string;
    planName: string;
    subscriptionId: string;
  }): Promise<void> {
    if (!this.enabled) return;

    try {
      await listmonkSyncService.ensureInitialized();
      const allContactsId = listmonkSyncService.getListId('All Contacts');
      const subscribersId = listmonkSyncService.getListId('Subscribers');
      if (!allContactsId || !subscribersId) return;

      await listmonkClient.upsertSubscriber(
        data.email,
        data.name,
        [allContactsId, subscribersId],
        {
          source: 'subscription',
          plan_name: data.planName,
          subscription_id: data.subscriptionId,
          last_synced: new Date().toISOString(),
        },
      );
      this.incrementCounter();
      logger.debug(`Listmonk event sync: subscription activated for ${data.email}`);
    } catch (err) {
      logger.debug('Listmonk event sync failed (onSubscriptionActivated):', err);
    }
  }

  /**
   * Sync a completed donation to Listmonk "All Contacts" + "Donors" lists.
   */
  async onDonationCompleted(data: {
    email: string;
    name: string;
    amountCents: number;
    orderId: string;
  }): Promise<void> {
    if (!this.enabled) return;

    try {
      await listmonkSyncService.ensureInitialized();
      const allContactsId = listmonkSyncService.getListId('All Contacts');
      const donorsId = listmonkSyncService.getListId('Donors');
      if (!allContactsId || !donorsId) return;

      await listmonkClient.upsertSubscriber(
        data.email,
        data.name,
        [allContactsId, donorsId],
        {
          source: 'donation',
          last_donation_amount: data.amountCents,
          last_order_id: data.orderId,
          last_synced: new Date().toISOString(),
        },
      );
      this.incrementCounter();
      logger.debug(`Listmonk event sync: donation completed for ${data.email}`);
    } catch (err) {
      logger.debug('Listmonk event sync failed (onDonationCompleted):', err);
    }
  }

  /**
   * Sync a product purchase to Listmonk "All Contacts" + "Donors" lists.
   */
  async onProductPurchased(data: {
    email: string;
    name: string;
    productTitle: string;
    amountCents: number;
    orderId: string;
  }): Promise<void> {
    if (!this.enabled) return;

    try {
      await listmonkSyncService.ensureInitialized();
      const allContactsId = listmonkSyncService.getListId('All Contacts');
      const donorsId = listmonkSyncService.getListId('Donors');
      if (!allContactsId || !donorsId) return;

      await listmonkClient.upsertSubscriber(
        data.email,
        data.name,
        [allContactsId, donorsId],
        {
          source: 'product_purchase',
          last_product: data.productTitle,
          last_purchase_amount: data.amountCents,
          last_order_id: data.orderId,
          last_synced: new Date().toISOString(),
        },
      );
      this.incrementCounter();
      logger.debug(`Listmonk event sync: product purchased for ${data.email}`);
    } catch (err) {
      logger.debug('Listmonk event sync failed (onProductPurchased):', err);
    }
  }

  /**
   * Sync an address update (e.g. support level change from canvass visit) to Listmonk lists.
   */
  async onAddressUpdated(data: {
    email: string;
    name: string;
    supportLevel?: string | null;
    sign?: boolean;
    address?: string | null;
  }): Promise<void> {
    if (!this.enabled) return;

    try {
      await listmonkSyncService.ensureInitialized();
      const allContactsId = listmonkSyncService.getListId('All Contacts');
      const locationsAllId = listmonkSyncService.getListId('Locations - All');
      if (!allContactsId || !locationsAllId) return;

      const listIds = [allContactsId, locationsAllId];

      // Add support level list
      if (data.supportLevel && SUPPORT_LEVEL_LIST_MAP[data.supportLevel]) {
        const levelListId = listmonkSyncService.getListId(SUPPORT_LEVEL_LIST_MAP[data.supportLevel]);
        if (levelListId) listIds.push(levelListId);
      }

      // Add sign list
      if (data.sign) {
        const signListId = listmonkSyncService.getListId('Has Campaign Sign');
        if (signListId) listIds.push(signListId);
      }

      await listmonkClient.upsertSubscriber(
        data.email,
        data.name,
        listIds,
        {
          source: 'canvass_visit',
          address: data.address || null,
          support_level: data.supportLevel || null,
          sign: data.sign ?? false,
          last_synced: new Date().toISOString(),
        },
      );
      this.incrementCounter();
      logger.debug(`Listmonk event sync: address updated for ${data.email}`);
    } catch (err) {
      logger.debug('Listmonk event sync failed (onAddressUpdated):', err);
    }
  }

  /**
   * Sync a re-engagement email send to Listmonk "All Contacts" + "Volunteers" lists.
   */
  async onReengagementSent(data: {
    email: string;
    name: string;
  }): Promise<void> {
    if (!this.enabled) return;

    try {
      await listmonkSyncService.ensureInitialized();
      const allContactsId = listmonkSyncService.getListId('All Contacts');
      const volunteersId = listmonkSyncService.getListId('Volunteers');
      if (!allContactsId || !volunteersId) return;

      await listmonkClient.upsertSubscriber(
        data.email,
        data.name,
        [allContactsId, volunteersId],
        {
          source: 'reengagement',
          last_reengagement_sent: new Date().toISOString(),
          last_synced: new Date().toISOString(),
        },
      );
      this.incrementCounter();
      logger.debug(`Listmonk event sync: reengagement sent for ${data.email}`);
    } catch (err) {
      logger.debug('Listmonk event sync failed (onReengagementSent):', err);
    }
  }

  /**
   * Sync tag changes on a contact to Listmonk lists associated with CRM tags.
   * For added tags: upsert subscriber to the corresponding Listmonk list.
   * For removed tags: remove subscriber from the corresponding Listmonk list.
   */
  async onContactTagsChanged(data: {
    email: string;
    name: string;
    addedTags: string[];
    removedTags: string[];
  }): Promise<void> {
    if (!this.enabled) return;

    try {
      // Lazy import to avoid circular dependency
      const { prisma } = await import('../config/database');

      // Find CRM tags that have listmonkListId set
      const allTagNames = [...data.addedTags, ...data.removedTags];
      if (allTagNames.length === 0) return;

      const crmTags = await prisma.crmTag.findMany({
        where: { name: { in: allTagNames }, listmonkListId: { not: null } },
      });
      const tagMap = new Map(crmTags.map(t => [t.name, t.listmonkListId!]));

      // Add to lists for added tags
      const addListIds = data.addedTags
        .map(t => tagMap.get(t))
        .filter((id): id is number => id != null);
      if (addListIds.length > 0) {
        await listmonkClient.upsertSubscriber(
          data.email,
          data.name,
          addListIds,
          { source: 'crm_tag', last_synced: new Date().toISOString() },
        );
        this.incrementCounter();
      }

      // Remove from lists for removed tags
      const removeListIds = data.removedTags
        .map(t => tagMap.get(t))
        .filter((id): id is number => id != null);
      if (removeListIds.length > 0) {
        const subscriber = await listmonkClient.findSubscriberByEmail(data.email);
        if (subscriber) {
          const currentListIds = subscriber.lists.map(l => l.id);
          await listmonkClient.removeSubscriberFromLists(
            subscriber.id,
            removeListIds,
            data.email,
            currentListIds,
          );
          this.incrementCounter();
        }
      }

      logger.debug(`Listmonk event sync: tags changed for ${data.email} (+${data.addedTags.length}/-${data.removedTags.length})`);
    } catch (err) {
      logger.debug('Listmonk event sync failed (onContactTagsChanged):', err);
    }
  }

  getStats(): {
    enabled: boolean;
    lastSyncAt: string | null;
    todaySyncCount: number;
  } {
    return {
      enabled: this.enabled,
      lastSyncAt: this._lastSyncAt?.toISOString() || null,
      todaySyncCount: this._todaySyncCount,
    };
  }
}

export const listmonkEventSyncService = new ListmonkEventSyncService();
