/**
 * CrmAdapter: the contract Mailforge uses to talk to ANY external CRM.
 *
 * Mailforge core never calls CRM systems directly. Instead it asks the
 * registered adapter (see `adapters.config.ts`). To support a new CRM, write a
 * class/object that implements this interface and register it in the config.
 */

import type { UserContext } from '@/core/types';
import type { Contact, ContactFilter, ConversionEvent } from '@/adapters/types';

export interface CrmAdapter {
  /** Logging/debug identifier, e.g. "cumas", "hubspot", "generic-rest". */
  readonly name: string;

  /**
   * Whether the adapter has the credentials it needs to operate. If false,
   * Mailforge will show a Settings-UI hint instead of trying live calls.
   */
  isConfigured(): boolean;

  /**
   * List contacts matching a filter. Pagination is the adapter's choice;
   * callers should pass `limit`/`offset` if they need it.
   */
  listContacts(filter: ContactFilter, ctx: UserContext): Promise<Contact[]>;

  /** Lookup a single contact by its external ID. */
  getContactById(id: string, ctx: UserContext): Promise<Contact | null>;

  /**
   * Poll conversion events that occurred since the given timestamp.
   * Used by the Worker's conversion-polling loop. Adapter is responsible for
   * rate-limiting / chunking if the underlying CRM is slow.
   */
  pollConversions(since: Date, ctx: UserContext): Promise<ConversionEvent[]>;

  /**
   * Optional: list available segments/tags/lists for the UI's recipient picker.
   * Adapters without a segment concept can return [].
   */
  listSegments?(ctx: UserContext): Promise<Array<{ id: string; label: string; count?: number }>>;

  /**
   * Optional: apply tags to a contact (e.g. "unsubscribed-newsletter").
   * Mailforge will only call this if the adapter declares the capability.
   */
  applyTags?(contactId: string, tags: string[], ctx: UserContext): Promise<void>;

  /**
   * Optional: resolve a per-recipient login/magic-link to insert into the email
   * body via the `{{LOGIN_LINK}}` placeholder. CRMs without a shop-login concept
   * should NOT implement this — the worker treats absence as "drop the tag".
   *
   * Inputs come from the workspace + recipient at send-time. Return an empty
   * string to skip the substitution (e.g. when the recipient has no externalId).
   */
  resolveLoginLink?(input: {
    workspaceShopDomain?: string | null;
    recipientExternalId?: string | null;
    /** "private" | "business" (or whatever the adapter's pool model needs). */
    recipientPoolType?: string | null;
    /** Mailforge-internal recipient ID — useful for attribution cookies. */
    recipientId: string;
  }): string;
}
