/**
 * Notify Step — user resolver registry.
 * Resolvers turn a raw PipelineContext field value into a ResolvedRecipient
 * (a Pulse user_id or an email address). notify.ts calls resolveRecipient()
 * to delegate to the appropriate resolver.
 *
 * Adding a new resolver: call registerResolver(name, fn) — no other files need
 * to change. All three v1 resolvers are registered at module load below.
 */

import { postgresClient } from '../postgres-client';
import { NotifyResolver, ResolvedRecipient, ResolverName } from '../../types/pipeline';

// ============================================================================
// v1 resolver implementations
// ============================================================================

/**
 * direct_email — the field value IS already an email string.
 * Validates format; returns { email } or null.
 */
const directEmail: NotifyResolver = async (fieldValue: unknown): Promise<ResolvedRecipient> => {
  if (typeof fieldValue !== 'string') return null;
  if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(fieldValue)) return null;
  return { email: fieldValue };
};

/**
 * pulse_user_id — the field value IS already a Pulse user.id string.
 * Checks existence in the "user" table; returns { user_id } or null.
 */
const pulseUserId: NotifyResolver = async (fieldValue: unknown): Promise<ResolvedRecipient> => {
  if (typeof fieldValue !== 'string' || fieldValue === '') return null;
  const res = await postgresClient.query<{ id: string }>(
    `SELECT id FROM "user" WHERE id = $1`,
    [fieldValue],
  );
  if (res.rows.length === 0) return null;
  return { user_id: res.rows[0].id };
};

/**
 * autotask_resource_email — the field value is an Autotask resource ID
 * (integer or numeric string). Looks up the resource's email in the
 * resources table; returns { email } or null.
 */
const autotaskResourceEmail: NotifyResolver = async (fieldValue: unknown): Promise<ResolvedRecipient> => {
  let resourceId: number;
  if (typeof fieldValue === 'number') {
    resourceId = fieldValue;
  } else if (typeof fieldValue === 'string' && /^\d+$/.test(fieldValue)) {
    resourceId = parseInt(fieldValue, 10);
  } else {
    return null;
  }

  const res = await postgresClient.query<{ email: string }>(
    `SELECT email FROM resources WHERE id = $1`,
    [resourceId],
  );
  if (res.rows.length === 0 || !res.rows[0].email) return null;
  return { email: res.rows[0].email };
};

// ============================================================================
// Resolver registry
// ============================================================================

export const RESOLVERS: Map<string, NotifyResolver> = new Map([
  ['direct_email', directEmail],
  ['pulse_user_id', pulseUserId],
  ['autotask_resource_email', autotaskResourceEmail],
]);

/**
 * Register a custom resolver at runtime (e.g., from a plugin or test).
 * Overwrites any existing resolver with the same name.
 */
export function registerResolver(name: ResolverName, fn: NotifyResolver): void {
  RESOLVERS.set(name, fn);
}

/**
 * Resolve a field value to a ResolvedRecipient using the named resolver.
 * Returns resolverFound=false when the name is not in the registry.
 * Catches resolver errors and returns recipient=null with resolverFound=true.
 */
export async function resolveRecipient(
  name: ResolverName,
  fieldValue: unknown,
): Promise<{ recipient: ResolvedRecipient; resolverFound: boolean }> {
  const fn = RESOLVERS.get(name);
  if (!fn) {
    return { recipient: null, resolverFound: false };
  }
  try {
    const recipient = await fn(fieldValue);
    return { recipient, resolverFound: true };
  } catch {
    // Resolver found but threw — treat as lookup failure, not registry miss
    return { recipient: null, resolverFound: true };
  }
}
