import { randomUUID } from 'crypto';
import prisma from '../config/db';
import { findFallbackPatientPortalAccessByPatientId } from './patient-account.store';
import {
  buildCommunicationProviderStatuses,
  deliverExternalCommunication,
  type CommunicationDeliveryMode,
  type CommunicationProvider,
  type CommunicationProviderStatus,
} from './communication-delivery.service';
import { readPersistentDataFile, writePersistentDataFile } from './local-store';

export type NotificationChannel = 'IN_APP' | 'EMAIL' | 'SMS' | 'WHATSAPP';
export type CommunicationStatus = 'SENT' | 'SIMULATED' | 'SKIPPED' | 'FAILED';
export type AppointmentCommunicationEvent =
  | 'APPOINTMENT_CREATED'
  | 'APPOINTMENT_UPDATED'
  | 'APPOINTMENT_RESCHEDULED'
  | 'APPOINTMENT_STATUS_CHANGED';

type StoredNotification = {
  id: string;
  recipientUserId: string | null;
  category: 'APPOINTMENT' | 'SYSTEM';
  title: string;
  message: string;
  actionLabel: string | null;
  actionPath: string | null;
  channel: 'IN_APP';
  readAt: string | null;
  createdAt: string;
};

type CommunicationChannels = {
  inApp: boolean;
  email: boolean;
  sms: boolean;
  whatsapp: boolean;
};

type StoredCommunicationSettings = {
  autoNotifyPatient: boolean;
  autoNotifyDentist: boolean;
  channels: CommunicationChannels;
  updatedAt: string;
};

export type AppointmentCommunicationSettings = StoredCommunicationSettings & {
  externalDeliveryMode: CommunicationDeliveryMode;
  providerStatus: {
    inApp: CommunicationProviderStatus;
    email: CommunicationProviderStatus;
    sms: CommunicationProviderStatus;
    whatsapp: CommunicationProviderStatus;
  };
};

export type AppointmentCommunicationLog = {
  id: string;
  eventType: AppointmentCommunicationEvent;
  recipientName: string;
  recipientRole: 'PATIENT' | 'DENTIST' | 'SYSTEM';
  recipientUserId: string | null;
  channel: NotificationChannel;
  provider: CommunicationProvider;
  destination: string | null;
  status: CommunicationStatus;
  title: string;
  message: string;
  detail: string | null;
  errorMessage: string | null;
  providerMessageId: string | null;
  createdAt: string;
};

export type AppointmentReminderStage = 'DAY_BEFORE' | 'TWO_HOURS_BEFORE';

type StoredCommunicationLog = Partial<AppointmentCommunicationLog> & {
  eventType?: string;
  recipientRole?: string;
  channel?: string;
  provider?: string;
  status?: string;
};

type RecordAppointmentCommunicationsInput = {
  eventType: AppointmentCommunicationEvent;
  appointmentId: string;
  startsAt: string;
  patient: {
    id: string;
    name: string;
    email: string | null;
    phone: string | null;
  };
  dentist: {
    id: string;
    name: string;
    email: string | null;
  };
  motivo: string;
  status: string;
  sourceMode: 'database' | 'local-fallback';
  reason?: string | null;
};

type RecordAppointmentReminderCommunicationsInput = {
  appointmentId: string;
  startsAt: string;
  patient: {
    id: string;
    name: string;
    email: string | null;
    phone: string | null;
  };
  dentist: {
    id: string;
    name: string;
    email: string | null;
  };
  motivo: string;
  status: string;
  stage: AppointmentReminderStage;
  sourceMode: 'database' | 'local-fallback';
};

const communicationSettingsFileName = 'communication-settings.json';
const communicationLogsFileName = 'communication-logs.json';
const notificationFeedFileName = 'notification-feed.json';

const formatDateTime = (value: string) =>
  new Intl.DateTimeFormat('es-PE', {
    timeZone: 'America/Lima',
    day: '2-digit',
    month: 'short',
    year: 'numeric',
    hour: '2-digit',
    minute: '2-digit',
  }).format(new Date(value));

const defaultCommunicationChannels = (): CommunicationChannels => ({
  inApp: true,
  email: false,
  sms: false,
  whatsapp: false,
});

const defaultStoredCommunicationSettings = (): StoredCommunicationSettings => ({
  autoNotifyPatient: false,
  autoNotifyDentist: true,
  channels: defaultCommunicationChannels(),
  updatedAt: new Date().toISOString(),
});

const defaultCommunicationLogs = () => [] as AppointmentCommunicationLog[];
const defaultNotificationFeed = () => [] as StoredNotification[];

const normalizeChannels = (channels?: Partial<CommunicationChannels> | null): CommunicationChannels => ({
  ...defaultCommunicationChannels(),
  ...(channels ?? {}),
});

const normalizeStoredSettings = (
  storedSettings?: Partial<StoredCommunicationSettings> | null,
): StoredCommunicationSettings => ({
  autoNotifyPatient: storedSettings?.autoNotifyPatient ?? true,
  autoNotifyDentist: storedSettings?.autoNotifyDentist ?? true,
  channels: normalizeChannels(storedSettings?.channels),
  updatedAt: storedSettings?.updatedAt ?? new Date().toISOString(),
});

const hydrateCommunicationSettings = (storedSettings: StoredCommunicationSettings): AppointmentCommunicationSettings => {
  const { providerStatus, externalDeliveryMode } = buildCommunicationProviderStatuses(storedSettings.channels);

  return {
    ...storedSettings,
    externalDeliveryMode,
    providerStatus,
  };
};

const normalizeEventType = (value: string | undefined): AppointmentCommunicationEvent => {
  switch (value) {
    case 'APPOINTMENT_CREATED':
    case 'APPOINTMENT_RESCHEDULED':
    case 'APPOINTMENT_STATUS_CHANGED':
    case 'APPOINTMENT_UPDATED':
      return value;
    default:
      return 'APPOINTMENT_UPDATED';
  }
};

const normalizeChannel = (value: string | undefined): NotificationChannel => {
  switch (value) {
    case 'IN_APP':
    case 'EMAIL':
    case 'SMS':
    case 'WHATSAPP':
      return value;
    default:
      return 'IN_APP';
  }
};

const normalizeRecipientRole = (value: string | undefined): AppointmentCommunicationLog['recipientRole'] => {
  switch (value) {
    case 'PATIENT':
    case 'DENTIST':
    case 'SYSTEM':
      return value;
    default:
      return 'SYSTEM';
  }
};

const normalizeStatus = (value: string | undefined): CommunicationStatus => {
  switch (value) {
    case 'SENT':
    case 'SIMULATED':
    case 'SKIPPED':
    case 'FAILED':
      return value;
    default:
      return 'SIMULATED';
  }
};

const inferProviderFromChannel = (channel: NotificationChannel): CommunicationProvider => {
  switch (channel) {
    case 'EMAIL':
      return 'RESEND';
    case 'SMS':
      return 'TWILIO';
    case 'WHATSAPP':
      return 'WHATSAPP_CLOUD';
    default:
      return 'INTERNAL_PANEL';
  }
};

const normalizeProvider = (value: string | undefined, channel: NotificationChannel): CommunicationProvider => {
  switch (value) {
    case 'INTERNAL_PANEL':
    case 'RESEND':
    case 'TWILIO':
    case 'WHATSAPP_CLOUD':
      return value;
    default:
      return inferProviderFromChannel(channel);
  }
};

const normalizeCommunicationLog = (log: StoredCommunicationLog): AppointmentCommunicationLog => {
  const channel = normalizeChannel(log.channel);

  return {
    id: typeof log.id === 'string' && log.id ? log.id : randomUUID(),
    eventType: normalizeEventType(log.eventType),
    recipientName: typeof log.recipientName === 'string' && log.recipientName ? log.recipientName : 'Sin destinatario',
    recipientRole: normalizeRecipientRole(log.recipientRole),
    recipientUserId: typeof log.recipientUserId === 'string' ? log.recipientUserId : null,
    channel,
    provider: normalizeProvider(log.provider, channel),
    destination: typeof log.destination === 'string' ? log.destination : null,
    status: normalizeStatus(log.status),
    title: typeof log.title === 'string' ? log.title : 'Comunicacion automatica',
    message: typeof log.message === 'string' ? log.message : 'Sin detalle disponible.',
    detail: typeof log.detail === 'string' ? log.detail : null,
    errorMessage: typeof log.errorMessage === 'string' ? log.errorMessage : null,
    providerMessageId: typeof log.providerMessageId === 'string' ? log.providerMessageId : null,
    createdAt: typeof log.createdAt === 'string' ? log.createdAt : new Date().toISOString(),
  };
};

const readNotifications = async () =>
  readPersistentDataFile<StoredNotification[]>(notificationFeedFileName, defaultNotificationFeed);

const writeNotifications = async (notifications: StoredNotification[]) =>
  writePersistentDataFile(notificationFeedFileName, notifications);

const readCommunicationLogs = async () => {
  const storedLogs = await readPersistentDataFile<StoredCommunicationLog[]>(
    communicationLogsFileName,
    defaultCommunicationLogs,
  );
  return storedLogs.map(normalizeCommunicationLog);
};

const writeCommunicationLogs = async (logs: AppointmentCommunicationLog[]) =>
  writePersistentDataFile(communicationLogsFileName, logs);

const readStoredCommunicationSettings = async () => {
  const storedSettings = await readPersistentDataFile<Partial<StoredCommunicationSettings>>(
    communicationSettingsFileName,
    defaultStoredCommunicationSettings,
  );

  return normalizeStoredSettings(storedSettings);
};

export const resolvePatientPortalNotificationRecipientId = async (patientId: string) => {
  try {
    const account = await prisma.patientPortalAccess.findFirst({
      where: {
        patientId,
        active: true,
      },
      select: {
        id: true,
      },
    });

    if (account) {
      return account.id;
    }
  } catch (error) {
    console.warn(
      'Database unavailable while resolving patient portal notification recipient, using local fallback store.',
      error,
    );
  }

  const fallbackAccount = await findFallbackPatientPortalAccessByPatientId(patientId);
  return fallbackAccount?.active ? fallbackAccount.id : null;
};

export const getAppointmentCommunicationSettings = async () => {
  const storedSettings = await readStoredCommunicationSettings();
  return hydrateCommunicationSettings(storedSettings);
};

export const updateAppointmentCommunicationSettings = async (
  input: Partial<AppointmentCommunicationSettings>,
) => {
  const currentSettings = await readStoredCommunicationSettings();

  const nextStoredSettings: StoredCommunicationSettings = {
    autoNotifyPatient: input.autoNotifyPatient ?? currentSettings.autoNotifyPatient,
    autoNotifyDentist: input.autoNotifyDentist ?? currentSettings.autoNotifyDentist,
    channels: normalizeChannels({
      ...currentSettings.channels,
      ...(input.channels ?? {}),
    }),
    updatedAt: new Date().toISOString(),
  };

  await writePersistentDataFile(communicationSettingsFileName, nextStoredSettings);
  return hydrateCommunicationSettings(nextStoredSettings);
};

export const listCommunicationLogs = async (limit = 20) => {
  const logs = await readCommunicationLogs();
  return logs
    .slice()
    .sort((left, right) => new Date(right.createdAt).getTime() - new Date(left.createdAt).getTime())
    .slice(0, limit);
};

export const listNotificationFeed = async (
  recipientUserId?: string | null,
  options?: { includeBroadcast?: boolean },
) => {
  const includeBroadcast = options?.includeBroadcast ?? true;
  const notifications = await readNotifications();

  return notifications
    .filter((notification) => {
      if (recipientUserId) {
        return (
          notification.recipientUserId === recipientUserId ||
          (includeBroadcast && notification.recipientUserId === null)
        );
      }

      return includeBroadcast ? notification.recipientUserId === null : false;
    })
    .sort((left, right) => new Date(right.createdAt).getTime() - new Date(left.createdAt).getTime())
    .slice(0, 20);
};

export const markNotificationAsRead = async (
  notificationId: string,
  recipientUserId?: string | null,
  options?: { allowBroadcast?: boolean },
) => {
  const allowBroadcast = options?.allowBroadcast ?? true;
  const notifications = await readNotifications();
  let didUpdate = false;

  const updatedNotifications = notifications.map((notification) => {
    if (notification.id !== notificationId) {
      return notification;
    }

    if (
      notification.recipientUserId !== null &&
      recipientUserId &&
      notification.recipientUserId !== recipientUserId
    ) {
      return notification;
    }

    if (notification.recipientUserId === null && !allowBroadcast) {
      return notification;
    }

    if (notification.readAt) {
      return notification;
    }

    didUpdate = true;
    return {
      ...notification,
      readAt: new Date().toISOString(),
    };
  });

  if (didUpdate) {
    await writeNotifications(updatedNotifications);
  }

  return didUpdate;
};

const getCommunicationContent = (
  input: RecordAppointmentCommunicationsInput,
  recipient: 'PATIENT' | 'DENTIST' | 'SYSTEM',
) => {
  const appointmentMoment = formatDateTime(input.startsAt);

  switch (input.eventType) {
    case 'APPOINTMENT_CREATED':
      return {
        title:
          recipient === 'PATIENT'
            ? 'Tu cita fue agendada'
            : recipient === 'DENTIST'
              ? 'Nueva cita asignada'
              : 'Nueva cita registrada',
        message:
          recipient === 'PATIENT'
            ? `Hola ${input.patient.name}, tu cita para ${input.motivo} fue agendada el ${appointmentMoment} con ${input.dentist.name}.`
            : recipient === 'DENTIST'
              ? `Se te asigno una cita de ${input.patient.name} para ${input.motivo} el ${appointmentMoment}.`
              : `Se registro una nueva cita para ${input.patient.name} con ${input.dentist.name} el ${appointmentMoment}.`,
      };
    case 'APPOINTMENT_RESCHEDULED':
      return {
        title:
          recipient === 'PATIENT'
            ? 'Tu cita fue reprogramada'
            : recipient === 'DENTIST'
              ? 'Cita reprogramada'
              : 'Cita reprogramada en agenda',
        message:
          recipient === 'PATIENT'
            ? `Tu cita para ${input.motivo} ahora sera el ${appointmentMoment} con ${input.dentist.name}.${input.reason ? ` Motivo: ${input.reason}.` : ''}`
            : recipient === 'DENTIST'
              ? `La cita de ${input.patient.name} fue reprogramada para el ${appointmentMoment}.${input.reason ? ` Motivo: ${input.reason}.` : ''}`
              : `Se reprogramo la cita de ${input.patient.name} para el ${appointmentMoment}.`,
      };
    case 'APPOINTMENT_STATUS_CHANGED':
      return {
        title:
          recipient === 'PATIENT'
            ? 'Actualizacion de tu cita'
            : recipient === 'DENTIST'
              ? 'Estado de cita actualizado'
              : 'Cambio de estado en agenda',
        message:
          recipient === 'PATIENT'
            ? `Tu cita para ${input.motivo} fue actualizada al estado ${input.status.toLowerCase()}.`
            : recipient === 'DENTIST'
              ? `La cita de ${input.patient.name} fue actualizada al estado ${input.status.toLowerCase()}.`
              : `La cita de ${input.patient.name} paso al estado ${input.status.toLowerCase()}.`,
      };
    default:
      return {
        title:
          recipient === 'PATIENT'
            ? 'Tu cita fue actualizada'
            : recipient === 'DENTIST'
              ? 'Cita actualizada'
              : 'Actualizacion de agenda',
        message:
          recipient === 'PATIENT'
            ? `Se actualizaron los datos de tu cita del ${appointmentMoment} para ${input.motivo}.`
            : recipient === 'DENTIST'
              ? `Se actualizaron los datos de la cita de ${input.patient.name} para ${appointmentMoment}.`
              : `Se actualizaron los datos de una cita de ${input.patient.name}.`,
      };
  }
};

const getReminderCommunicationContent = (
  input: RecordAppointmentReminderCommunicationsInput,
  recipient: 'PATIENT' | 'DENTIST' | 'SYSTEM',
) => {
  const appointmentMoment = formatDateTime(input.startsAt);
  const stageLabel = input.stage === 'DAY_BEFORE' ? 'manana' : 'en 2 horas';

  switch (recipient) {
    case 'PATIENT':
      return {
        title: input.stage === 'DAY_BEFORE' ? 'Recuerda tu cita de manana' : 'Tu cita es muy pronto',
        message: `Hola ${input.patient.name}, recuerda tu cita ${stageLabel} por ${input.motivo} con ${input.dentist.name} el ${appointmentMoment}.`,
      };
    case 'DENTIST':
      return {
        title: input.stage === 'DAY_BEFORE' ? 'Recordatorio de cita para manana' : 'Recordatorio de cita proxima',
        message: `Tienes una cita ${stageLabel} con ${input.patient.name} por ${input.motivo} el ${appointmentMoment}.`,
      };
    default:
      return {
        title: input.stage === 'DAY_BEFORE' ? 'Recordatorio automatico de manana' : 'Recordatorio automatico proximo',
        message: `El sistema programo un recordatorio ${stageLabel} para la cita de ${input.patient.name} con ${input.dentist.name}.`,
      };
  }
};

const buildInternalLog = (
  input: RecordAppointmentCommunicationsInput,
  recipient: 'PATIENT' | 'DENTIST' | 'SYSTEM',
  content: { title: string; message: string },
  createdAt: string,
  options?: {
    recipientUserId?: string | null;
    destination?: string | null;
    detail?: string | null;
  },
): AppointmentCommunicationLog => ({
  id: randomUUID(),
  eventType: input.eventType,
  recipientName:
    recipient === 'PATIENT'
      ? input.patient.name
      : recipient === 'DENTIST'
        ? input.dentist.name
        : 'Panel administrativo',
  recipientRole: recipient,
  recipientUserId:
    options?.recipientUserId ?? (recipient === 'DENTIST' ? input.dentist.id : null),
  channel: 'IN_APP',
  provider: 'INTERNAL_PANEL',
  destination:
    options?.destination ??
    (recipient === 'PATIENT' ? input.patient.email : recipient === 'DENTIST' ? input.dentist.email : null),
  status: 'SENT',
  title: content.title,
  message: content.message,
  detail: options?.detail ?? 'Disponible en la campana del sistema.',
  errorMessage: null,
  providerMessageId: null,
  createdAt,
});

const buildExternalLog = async (
  input: RecordAppointmentCommunicationsInput,
  recipient: 'PATIENT' | 'DENTIST',
  channel: Exclude<NotificationChannel, 'IN_APP'>,
  destination: string | null,
) => {
  const content = getCommunicationContent(input, recipient);
  const delivery = await deliverExternalCommunication({
    channel,
    destination,
    title: content.title,
    message: content.message,
    eventType: input.eventType,
    appointmentId: input.appointmentId,
    startsAt: input.startsAt,
    motivo: input.motivo,
    status: input.status,
    recipientName: recipient === 'PATIENT' ? input.patient.name : input.dentist.name,
    recipientRole: recipient,
    dentistName: input.dentist.name,
    reason: input.reason ?? null,
  });

  return {
    id: randomUUID(),
    eventType: input.eventType,
    recipientName: recipient === 'PATIENT' ? input.patient.name : input.dentist.name,
    recipientRole: recipient,
    recipientUserId: recipient === 'DENTIST' ? input.dentist.id : null,
    channel,
    provider: delivery.provider,
    destination,
    status: delivery.status,
    title: content.title,
    message: content.message,
    detail: delivery.detail,
    errorMessage: delivery.errorMessage,
    providerMessageId: delivery.providerMessageId,
    createdAt: new Date().toISOString(),
  } satisfies AppointmentCommunicationLog;
};

export const recordAppointmentCommunications = async (input: RecordAppointmentCommunicationsInput) => {
  const settings = await getAppointmentCommunicationSettings();
  const now = new Date().toISOString();
  const notifications = await readNotifications();
  const logs = await readCommunicationLogs();
  const nextNotifications = [...notifications];
  const nextLogs = [...logs];
  const patientPortalRecipientId =
    settings.channels.inApp && settings.autoNotifyPatient
      ? await resolvePatientPortalNotificationRecipientId(input.patient.id)
      : null;

  if (settings.channels.inApp && settings.autoNotifyPatient && patientPortalRecipientId) {
    const patientContent = getCommunicationContent(input, 'PATIENT');
    nextNotifications.unshift({
      id: randomUUID(),
      recipientUserId: patientPortalRecipientId,
      category: 'APPOINTMENT',
      title: patientContent.title,
      message: patientContent.message,
      actionLabel: 'Ver cita',
      actionPath: '/portal#citas',
      channel: 'IN_APP',
      readAt: null,
      createdAt: now,
    });

    nextLogs.unshift(
      buildInternalLog(input, 'PATIENT', patientContent, now, {
        recipientUserId: patientPortalRecipientId,
        destination: input.patient.email,
        detail: 'Disponible en el portal del paciente.',
      }),
    );
  }

  if (settings.channels.inApp && settings.autoNotifyDentist) {
    const dentistContent = getCommunicationContent(input, 'DENTIST');
    nextNotifications.unshift({
      id: randomUUID(),
      recipientUserId: input.dentist.id,
      category: 'APPOINTMENT',
      title: dentistContent.title,
      message: dentistContent.message,
      actionLabel: 'Ver agenda',
      actionPath: '/agenda',
      channel: 'IN_APP',
      readAt: null,
      createdAt: now,
    });

    nextLogs.unshift(buildInternalLog(input, 'DENTIST', dentistContent, now));
  }

  if (settings.channels.inApp) {
    const systemContent = getCommunicationContent(input, 'SYSTEM');
    nextNotifications.unshift({
      id: randomUUID(),
      recipientUserId: null,
      category: 'SYSTEM',
      title: systemContent.title,
      message: `${systemContent.message} Fuente ${input.sourceMode}.`,
      actionLabel: 'Abrir agenda',
      actionPath: '/agenda',
      channel: 'IN_APP',
      readAt: null,
      createdAt: now,
    });

    nextLogs.unshift(
      buildInternalLog(
        input,
        'SYSTEM',
        {
          title: systemContent.title,
          message: `${systemContent.message} Fuente ${input.sourceMode}.`,
        },
        now,
      ),
    );
  }

  const externalTasks: Array<Promise<AppointmentCommunicationLog>> = [];

  if (settings.autoNotifyPatient) {
    if (settings.channels.email) {
      externalTasks.push(buildExternalLog(input, 'PATIENT', 'EMAIL', input.patient.email));
    }

    if (settings.channels.sms) {
      externalTasks.push(buildExternalLog(input, 'PATIENT', 'SMS', input.patient.phone));
    }

    if (settings.channels.whatsapp) {
      externalTasks.push(buildExternalLog(input, 'PATIENT', 'WHATSAPP', input.patient.phone));
    }
  }

  if (settings.autoNotifyDentist && settings.channels.email) {
    externalTasks.push(buildExternalLog(input, 'DENTIST', 'EMAIL', input.dentist.email));
  }

  if (externalTasks.length > 0) {
    const externalLogs = await Promise.all(externalTasks);
    nextLogs.unshift(...externalLogs);
  }

  await Promise.all([
    writeNotifications(nextNotifications.slice(0, 120)),
    writeCommunicationLogs(nextLogs.slice(0, 240)),
  ]);
};

export const recordAppointmentReminderCommunications = async (
  input: RecordAppointmentReminderCommunicationsInput,
) => {
  const settings = await getAppointmentCommunicationSettings();
  const now = new Date().toISOString();
  const notifications = await readNotifications();
  const logs = await readCommunicationLogs();
  const nextNotifications = [...notifications];
  const nextLogs = [...logs];
  const patientPortalRecipientId =
    settings.channels.inApp && settings.autoNotifyPatient
      ? await resolvePatientPortalNotificationRecipientId(input.patient.id)
      : null;

  if (settings.channels.inApp && settings.autoNotifyPatient && patientPortalRecipientId) {
    const patientContent = getReminderCommunicationContent(input, 'PATIENT');
    nextNotifications.unshift({
      id: randomUUID(),
      recipientUserId: patientPortalRecipientId,
      category: 'APPOINTMENT',
      title: patientContent.title,
      message: patientContent.message,
      actionLabel: 'Ver cita',
      actionPath: '/portal#citas',
      channel: 'IN_APP',
      readAt: null,
      createdAt: now,
    });

    nextLogs.unshift(
      buildInternalLog(
        {
          eventType: 'APPOINTMENT_UPDATED',
          appointmentId: input.appointmentId,
          startsAt: input.startsAt,
          patient: input.patient,
          dentist: input.dentist,
          motivo: input.motivo,
          status: input.status,
          sourceMode: input.sourceMode,
          reason: null,
        },
        'PATIENT',
        patientContent,
        now,
        {
          recipientUserId: patientPortalRecipientId,
          destination: input.patient.email,
          detail: 'Recordatorio disponible en el portal del paciente.',
        },
      ),
    );
  }

  if (settings.channels.inApp && settings.autoNotifyDentist) {
    const dentistContent = getReminderCommunicationContent(input, 'DENTIST');
    nextNotifications.unshift({
      id: randomUUID(),
      recipientUserId: input.dentist.id,
      category: 'APPOINTMENT',
      title: dentistContent.title,
      message: dentistContent.message,
      actionLabel: 'Ver agenda',
      actionPath: '/agenda',
      channel: 'IN_APP',
      readAt: null,
      createdAt: now,
    });

    nextLogs.unshift(
      buildInternalLog(
        {
          eventType: 'APPOINTMENT_UPDATED',
          appointmentId: input.appointmentId,
          startsAt: input.startsAt,
          patient: input.patient,
          dentist: input.dentist,
          motivo: input.motivo,
          status: input.status,
          sourceMode: input.sourceMode,
          reason: null,
        },
        'DENTIST',
        dentistContent,
        now,
        {
          detail: 'Recordatorio visible en la agenda interna.',
        },
      ),
    );
  }

  if (settings.channels.inApp) {
    const systemContent = getReminderCommunicationContent(input, 'SYSTEM');
    nextNotifications.unshift({
      id: randomUUID(),
      recipientUserId: null,
      category: 'SYSTEM',
      title: systemContent.title,
      message: `${systemContent.message} Fuente ${input.sourceMode}.`,
      actionLabel: 'Abrir agenda',
      actionPath: '/agenda',
      channel: 'IN_APP',
      readAt: null,
      createdAt: now,
    });

    nextLogs.unshift(
      buildInternalLog(
        {
          eventType: 'APPOINTMENT_UPDATED',
          appointmentId: input.appointmentId,
          startsAt: input.startsAt,
          patient: input.patient,
          dentist: input.dentist,
          motivo: input.motivo,
          status: input.status,
          sourceMode: input.sourceMode,
          reason: null,
        },
        'SYSTEM',
        {
          title: systemContent.title,
          message: `${systemContent.message} Fuente ${input.sourceMode}.`,
        },
        now,
        {
          detail: 'Recordatorio registrado en el panel administrativo.',
        },
      ),
    );
  }

  const externalTasks: Array<Promise<AppointmentCommunicationLog>> = [];

  if (settings.autoNotifyPatient) {
    const patientContent = getReminderCommunicationContent(input, 'PATIENT');

    if (settings.channels.email) {
      externalTasks.push(
        deliverExternalCommunication({
          channel: 'EMAIL',
          destination: input.patient.email,
          title: patientContent.title,
          message: patientContent.message,
          eventType: 'APPOINTMENT_UPDATED',
          appointmentId: input.appointmentId,
          startsAt: input.startsAt,
          motivo: input.motivo,
          status: input.status,
          recipientName: input.patient.name,
          recipientRole: 'PATIENT',
          dentistName: input.dentist.name,
          reason: null,
        }).then((delivery) => ({
          id: randomUUID(),
          eventType: 'APPOINTMENT_UPDATED',
          recipientName: input.patient.name,
          recipientRole: 'PATIENT',
          recipientUserId: null,
          channel: 'EMAIL',
          provider: delivery.provider,
          destination: input.patient.email,
          status: delivery.status,
          title: patientContent.title,
          message: patientContent.message,
          detail: delivery.detail,
          errorMessage: delivery.errorMessage,
          providerMessageId: delivery.providerMessageId,
          createdAt: new Date().toISOString(),
        })),
      );
    }

    if (settings.channels.sms) {
      externalTasks.push(
        deliverExternalCommunication({
          channel: 'SMS',
          destination: input.patient.phone,
          title: patientContent.title,
          message: patientContent.message,
          eventType: 'APPOINTMENT_UPDATED',
          appointmentId: input.appointmentId,
          startsAt: input.startsAt,
          motivo: input.motivo,
          status: input.status,
          recipientName: input.patient.name,
          recipientRole: 'PATIENT',
          dentistName: input.dentist.name,
          reason: null,
        }).then((delivery) => ({
          id: randomUUID(),
          eventType: 'APPOINTMENT_UPDATED',
          recipientName: input.patient.name,
          recipientRole: 'PATIENT',
          recipientUserId: null,
          channel: 'SMS',
          provider: delivery.provider,
          destination: input.patient.phone,
          status: delivery.status,
          title: patientContent.title,
          message: patientContent.message,
          detail: delivery.detail,
          errorMessage: delivery.errorMessage,
          providerMessageId: delivery.providerMessageId,
          createdAt: new Date().toISOString(),
        })),
      );
    }

    if (settings.channels.whatsapp) {
      externalTasks.push(
        deliverExternalCommunication({
          channel: 'WHATSAPP',
          destination: input.patient.phone,
          title: patientContent.title,
          message: patientContent.message,
          eventType: 'APPOINTMENT_UPDATED',
          appointmentId: input.appointmentId,
          startsAt: input.startsAt,
          motivo: input.motivo,
          status: input.status,
          recipientName: input.patient.name,
          recipientRole: 'PATIENT',
          dentistName: input.dentist.name,
          reason: null,
        }).then((delivery) => ({
          id: randomUUID(),
          eventType: 'APPOINTMENT_UPDATED',
          recipientName: input.patient.name,
          recipientRole: 'PATIENT',
          recipientUserId: null,
          channel: 'WHATSAPP',
          provider: delivery.provider,
          destination: input.patient.phone,
          status: delivery.status,
          title: patientContent.title,
          message: patientContent.message,
          detail: delivery.detail,
          errorMessage: delivery.errorMessage,
          providerMessageId: delivery.providerMessageId,
          createdAt: new Date().toISOString(),
        })),
      );
    }
  }

  if (settings.autoNotifyDentist && settings.channels.email) {
    const dentistContent = getReminderCommunicationContent(input, 'DENTIST');
    externalTasks.push(
      deliverExternalCommunication({
        channel: 'EMAIL',
        destination: input.dentist.email,
        title: dentistContent.title,
        message: dentistContent.message,
        eventType: 'APPOINTMENT_UPDATED',
        appointmentId: input.appointmentId,
        startsAt: input.startsAt,
        motivo: input.motivo,
        status: input.status,
        recipientName: input.dentist.name,
        recipientRole: 'DENTIST',
        dentistName: input.dentist.name,
        reason: null,
      }).then((delivery) => ({
        id: randomUUID(),
        eventType: 'APPOINTMENT_UPDATED',
        recipientName: input.dentist.name,
        recipientRole: 'DENTIST',
        recipientUserId: input.dentist.id,
        channel: 'EMAIL',
        provider: delivery.provider,
        destination: input.dentist.email,
        status: delivery.status,
        title: dentistContent.title,
        message: dentistContent.message,
        detail: delivery.detail,
        errorMessage: delivery.errorMessage,
        providerMessageId: delivery.providerMessageId,
        createdAt: new Date().toISOString(),
      })),
    );
  }

  if (externalTasks.length > 0) {
    const externalLogs = await Promise.all(externalTasks);
    nextLogs.unshift(...externalLogs);
  }

  await Promise.all([
    writeNotifications(nextNotifications.slice(0, 160)),
    writeCommunicationLogs(nextLogs.slice(0, 320)),
  ]);
};
