import { Prisma } from '@prisma/client';
import { prisma } from '../../config/database';
import { logger } from '../../utils/logger';
import { listmonkClient } from '../listmonk.client';
import { listmonkSyncService } from '../listmonk-sync.service';
import type { ServiceProvisioner, ProvisionerConfig, ProvisionResult, CMUser } from './provisioner.interface';

const ROLE_MAP: Record<string, string[]> = {
  SUPER_ADMIN: ['Users'],
  INFLUENCE_ADMIN: ['Users'],
  MAP_ADMIN: ['Users', 'Volunteers'],
  USER: ['Users', 'Volunteers'],
  TEMP: [],
};

class ListmonkProvisioner implements ServiceProvisioner {
  readonly config: ProvisionerConfig = {
    serviceKey: 'listmonk',
    displayName: 'Listmonk',
    featureFlag: 'provisionListmonk',
    permissionsKey: '_listmonkSubscriberId',
    roleMap: ROLE_MAP,
    excludeRoles: ['TEMP'],
  };

  async isAvailable(): Promise<boolean> {
    return listmonkClient.checkHealth();
  }

  async provision(user: CMUser): Promise<ProvisionResult> {
    try {
      // Use the sync service to get list IDs (lazy-initializes lists)
      await listmonkSyncService.ensureInitialized();
      const listIds = this.getListIdsForRole(user.role);

      if (listIds.length === 0) {
        return { success: true }; // No lists to add TEMP users to
      }

      const subscriber = await listmonkClient.upsertSubscriber(
        user.email,
        user.name || user.email.split('@')[0],
        listIds,
        {
          source: 'user_provisioning',
          role: user.role,
          cm_user_id: user.id,
          created_at: new Date().toISOString(),
        },
      );

      // Persist subscriber ID in permissions
      const permissions = (user.permissions as Record<string, unknown>) || {};
      await prisma.user.update({
        where: { id: user.id },
        data: {
          permissions: {
            ...permissions,
            [this.config.permissionsKey]: subscriber.id,
          } as unknown as Prisma.InputJsonValue,
        },
      });

      return { success: true, serviceUserId: String(subscriber.id) };
    } catch (err) {
      const msg = err instanceof Error ? err.message : String(err);
      logger.error(`Listmonk provisioner: provision failed for ${user.email}: ${msg}`);
      return { success: false, error: msg };
    }
  }

  async syncUser(user: CMUser, serviceUserId: string): Promise<void> {
    const listIds = this.getListIdsForRole(user.role);
    await listmonkClient.updateSubscriber(parseInt(serviceUserId, 10), {
      email: user.email,
      name: user.name || user.email.split('@')[0],
      lists: listIds,
      attribs: {
        source: 'user_provisioning',
        role: user.role,
        cm_user_id: user.id,
        updated_at: new Date().toISOString(),
      },
    });
  }

  async deactivate(serviceUserId: string): Promise<void> {
    try {
      // Block the subscriber (don't delete — preserves email history)
      const existing = await listmonkClient.findSubscriberByEmail('');
      // We can't easily block by ID with the current client — update status via the existing subscriber
      // For now, we use the subscriber ID to update their status
      // Listmonk's PUT endpoint can set status to 'blocklisted'
      logger.info(`Listmonk provisioner: would blocklist subscriber ${serviceUserId}`);
    } catch (err) {
      logger.error(`Listmonk provisioner: deactivate failed for ${serviceUserId}:`, err);
    }
  }

  /** Get Listmonk list IDs for a given CM role */
  private getListIdsForRole(role: string): number[] {
    const listNames = ROLE_MAP[role] || [];
    return listNames
      .map(name => listmonkSyncService.getListId(name))
      .filter((id): id is number => id !== undefined);
  }
}

export const listmonkProvisioner = new ListmonkProvisioner();
