/**
 * Form 16 notification triggers: 26AS added, success/unsuccess, alerts, reminders.
 * Uses Form 16 admin config templates and notificationService.sendToUsers.
 */

import { Op } from 'sequelize';
import { User } from '@models/User';
import { Dealer } from '@models/Dealer';
import { Form16CreditNote } from '@models/Form16CreditNote';
import { Form16aSubmission } from '@models/Form16aSubmission';
import { WorkflowRequest } from '@models/WorkflowRequest';
import { getForm16Config } from './form16Config.service';
import logger from '@utils/logger';

/** Get user IDs for dealers (principal email linked to a dealer). */
export async function getDealerUserIds(): Promise<string[]> {
  const dealers = await Dealer.findAll({
    where: { isActive: true },
    attributes: ['dealerPrincipalEmailId'],
    raw: true,
  });
  const emails = [...new Set((dealers.map((d) => (d as any).dealerPrincipalEmailId).filter(Boolean) as string[]).map((e) => e.trim().toLowerCase()))];
  if (emails.length === 0) return [];
  const users = await User.findAll({
    where: userWhereEmailIn(emails),
    attributes: ['userId'],
    raw: true,
  });
  return users.map((u) => (u as any).userId);
}

function userWhereEmailIn(emails: string[]) {
  if (emails.length === 0) return { email: { [Op.eq]: '__no_match__' } } as any;
  return { [Op.or]: emails.map((e) => ({ email: { [Op.iLike]: e } })) };
}

/** Get user IDs who should receive 26AS-added notification (RE side): config list or all non-dealer users. */
export async function getReUserIdsFor26As(): Promise<string[]> {
  const config = await getForm16Config();
  const viewerEmails = config.twentySixAsViewerEmails || [];
  if (viewerEmails.length > 0) {
    const users = await User.findAll({
      where: userWhereEmailIn(viewerEmails),
      attributes: ['userId'],
      raw: true,
    });
    return users.map((u) => (u as any).userId);
  }
  const dealerIds = await getDealerUserIds();
  const allUsers = await User.findAll({ attributes: ['userId'], raw: true });
  const allIds = allUsers.map((u) => (u as any).userId);
  if (dealerIds.length === 0) return allIds;
  const dealerSet = new Set(dealerIds);
  return allIds.filter((id) => !dealerSet.has(id));
}

/**
 * Trigger notifications when 26AS data is uploaded: sent only to RE users (admins / 26AS viewers / submission viewers).
 * Dealers no longer receive this notification.
 */
export async function trigger26AsDataAddedNotification(): Promise<void> {
  try {
    const config = await getForm16Config();
    const n = config.notification26AsDataAdded;
    if (!n?.enabled) {
      logger.info('[Form16Notification] 26AS notification disabled in config, skipping');
      return;
    }
    const { notificationService } = await import('./notification.service');

    // Base RE audience (admins / RE viewers). This helper already tries to exclude dealers,
    // but we defensively re-filter below so that 26AS notifications are never sent to dealers.
    const baseReUserIds = await getReUserIdsFor26As();
    const dealerUserIds = await getDealerUserIds();
    const dealerSet = new Set(dealerUserIds);
    const reUserIds = baseReUserIds.filter((id) => !dealerSet.has(id));

    const title = 'Form 16 – 26AS data updated';
    if (reUserIds.length > 0 && n.templateRe) {
      await notificationService.sendToUsers(reUserIds, {
        title,
        body: n.templateRe,
        type: 'form16_26as_added',
      });
      logger.info(`[Form16Notification] 26AS notification sent to ${reUserIds.length} RE user(s)`);
    }
  } catch (e) {
    logger.error('[Form16Notification] trigger26AsDataAddedNotification failed:', e);
  }
}

/** Replace [CreditNoteRef] / [Issue] in template. */
function replacePlaceholders(template: string, replacements: Record<string, string>): string {
  let out = template;
  for (const [key, value] of Object.entries(replacements)) {
    out = out.replace(new RegExp(`\\[${key}\\]`, 'gi'), value);
  }
  return out;
}

/**
 * Notify the dealer (initiator) after Form 16 submission result: success (credit note) or unsuccessful.
 */
export async function triggerForm16SubmissionResultNotification(
  initiatorUserId: string,
  validationStatus: string | undefined,
  opts: { creditNoteNumber?: string | null; requestId?: string; validationNotes?: string }
): Promise<void> {
  try {
    const config = await getForm16Config();
    const { notificationService } = await import('./notification.service');
    const isSuccess =
      validationStatus === 'success' || validationStatus === 'manually_approved';
    if (isSuccess && config.notificationForm16SuccessCreditNote?.enabled && config.notificationForm16SuccessCreditNote.template) {
      const body = replacePlaceholders(config.notificationForm16SuccessCreditNote.template, {
        CreditNoteRef: opts.creditNoteNumber || '—',
      });
      await notificationService.sendToUsers([initiatorUserId], {
        title: 'Form 16 – Credit note issued',
        body,
        type: 'form16_success_credit_note',
        requestId: opts.requestId,
      });
      logger.info(`[Form16Notification] Success notification sent to initiator ${initiatorUserId}`);
      return;
    }
    if (
      !isSuccess &&
      (validationStatus === 'failed' || validationStatus === 'duplicate' || validationStatus === 'resubmission_needed') &&
      config.notificationForm16Unsuccessful?.enabled &&
      config.notificationForm16Unsuccessful.template
    ) {
      const issue = opts.validationNotes || 'Submission could not be processed. Please check the request and resubmit if needed.';
      const body = replacePlaceholders(config.notificationForm16Unsuccessful.template, { Issue: issue });
      await notificationService.sendToUsers([initiatorUserId], {
        title: 'Form 16 – Submission unsuccessful',
        body,
        type: 'form16_unsuccessful',
        requestId: opts.requestId,
      });
      logger.info(`[Form16Notification] Unsuccessful notification sent to initiator ${initiatorUserId}`);
    }
  } catch (e) {
    logger.error('[Form16Notification] triggerForm16SubmissionResultNotification failed:', e);
  }
}

/**
 * Notify dealer when RE marks submission as resubmission needed or cancels (unsuccessful outcome).
 */
export async function triggerForm16UnsuccessfulByRequestId(requestId: string, issueMessage: string): Promise<void> {
  try {
    const req = await WorkflowRequest.findByPk(requestId, { attributes: ['initiatorId'], raw: true });
    const initiatorId = (req as any)?.initiatorId;
    if (!initiatorId) return;
    await triggerForm16SubmissionResultNotification(initiatorId, 'resubmission_needed', {
      requestId,
      validationNotes: issueMessage,
    });
  } catch (e) {
    logger.error('[Form16Notification] triggerForm16UnsuccessfulByRequestId failed:', e);
  }
}

/**
 * Send "submit Form 16" alert to dealers (e.g. those who haven't submitted for a quarter).
 * Call from a scheduled job using config alertSubmitForm16FrequencyDays/Hours.
 */
export async function triggerForm16AlertSubmit(dealerUserIds: string[], placeholders?: { name?: string; dueDate?: string }): Promise<void> {
  if (dealerUserIds.length === 0) return;
  try {
    const config = await getForm16Config();
    if (!config.alertSubmitForm16Enabled || !config.alertSubmitForm16Template) return;
    const body = replacePlaceholders(config.alertSubmitForm16Template, {
      Name: placeholders?.name ?? 'Dealer',
      DueDate: placeholders?.dueDate ?? '—',
    });
    const { notificationService } = await import('./notification.service');
    await notificationService.sendToUsers(dealerUserIds, {
      title: 'Form 16 – Submit required',
      body,
      type: 'form16_alert_submit',
    });
    logger.info(`[Form16Notification] Alert submit sent to ${dealerUserIds.length} dealer(s)`);
  } catch (e) {
    logger.error('[Form16Notification] triggerForm16AlertSubmit failed:', e);
  }
}

/**
 * Send pending Form 16 reminder to dealers (e.g. those with pending submission).
 * Call from a scheduled job using config reminderFrequencyDays/Hours.
 */
export async function triggerForm16Reminder(dealerUserIds: string[], placeholders?: { name?: string; requestId?: string }): Promise<void> {
  if (dealerUserIds.length === 0) return;
  try {
    const config = await getForm16Config();
    if (!config.reminderNotificationEnabled || !config.reminderNotificationTemplate) return;
    const body = replacePlaceholders(config.reminderNotificationTemplate, {
      Name: placeholders?.name ?? 'Dealer',
      'Request ID': placeholders?.requestId ?? '—',
    });
    const { notificationService } = await import('./notification.service');
    await notificationService.sendToUsers(dealerUserIds, {
      title: 'Form 16 – Pending submission reminder',
      body,
      type: 'form16_reminder',
    });
    logger.info(`[Form16Notification] Reminder sent to ${dealerUserIds.length} dealer(s)`);
  } catch (e) {
    logger.error('[Form16Notification] triggerForm16Reminder failed:', e);
  }
}

/**
 * Scheduled job: send "submit Form 16" alert to all non-submitted dealers (current FY).
 * Call from cron (e.g. daily at 9 AM).
 */
export async function runForm16AlertSubmitJob(): Promise<void> {
  try {
    const config = await getForm16Config();
    if (!config.alertSubmitForm16Enabled) {
      logger.info('[Form16Notification] Alert submit disabled in config, skipping job');
      return;
    }
    const { getDealerUserIdsFromNonSubmittedDealers } = await import('./form16.service');
    const dealerUserIds = await getDealerUserIdsFromNonSubmittedDealers();
    if (dealerUserIds.length === 0) {
      logger.info('[Form16Notification] No non-submitted dealers for alert, skipping');
      return;
    }
    const y = new Date().getFullYear();
    const fy = `${y}-${(y + 1).toString().slice(-2)}`;
    const dueDate = `FY ${fy} (as per policy)`;
    await triggerForm16AlertSubmit(dealerUserIds, { name: 'Dealer', dueDate });
    logger.info(`[Form16Notification] Alert submit job completed: notified ${dealerUserIds.length} dealer(s)`);
  } catch (e) {
    logger.error('[Form16Notification] runForm16AlertSubmitJob failed:', e);
  }
}

/**
 * Scheduled job: send "pending Form 16" reminder to dealers who have open submissions without credit note.
 * Call from cron (e.g. daily at 10 AM).
 */
export async function runForm16ReminderJob(): Promise<void> {
  try {
    const config = await getForm16Config();
    if (!config.reminderNotificationEnabled) {
      logger.info('[Form16Notification] Reminder disabled in config, skipping job');
      return;
    }
    const { getDealersWithPendingForm16Submissions } = await import('./form16.service');
    const pending = await getDealersWithPendingForm16Submissions();
    if (pending.length === 0) {
      logger.info('[Form16Notification] No pending Form 16 submissions for reminder, skipping');
      return;
    }
    for (const { userId, requestId } of pending) {
      await triggerForm16Reminder([userId], { name: 'Dealer', requestId });
    }
    logger.info(`[Form16Notification] Reminder job completed: sent ${pending.length} reminder(s)`);
  } catch (e) {
    logger.error('[Form16Notification] runForm16ReminderJob failed:', e);
  }
}
