/**
 * Real Estate Notification Service
 *
 * Handles notifications for property viewings, matches, price drops, and
 * status changes. Sends WhatsApp messages via the WhatsApp Business API
 * through the shared whatsappService singleton.
 *
 * Each function:
 *   - Checks duplicate prevention (won't re-send if already sent)
 *   - Logs notification details
 *   - Sends the message via WhatsApp (best-effort / non-blocking)
 *   - Updates the corresponding sentAt timestamp regardless of send outcome
 *   - Returns { sent: boolean, reason?: string }
 *
 * @module services/real-estate/notification
 */

import { prisma } from '@/lib/db';
import { logger } from '@/lib/logger';
import { whatsappService } from '@/lib/services/whatsapp.service';

const log = logger.scope('RealEstateNotifications');

/**
 * Format a Date object into a human-readable viewing date string.
 * Example output: "Monday, January 15, 2026"
 */
function formatViewingDate(date: Date): string {
  return date.toLocaleDateString('en-US', {
    weekday: 'long',
    year: 'numeric',
    month: 'long',
    day: 'numeric',
  });
}

// ── Return type ──────────────────────────────────────────────────────────────

interface NotificationResult {
  sent: boolean;
  reason?: string;
}

// ── Viewing notifications ────────────────────────────────────────────────────

/**
 * Send a viewing confirmation notification to the client via WhatsApp.
 *
 * Checks confirmationSentAt to prevent duplicate sends.
 *
 * @param viewingId - PropertyViewing record ID
 * @returns Whether the notification was sent
 */
export async function sendViewingConfirmation(
  viewingId: string,
): Promise<NotificationResult> {
  const viewing = await prisma.propertyViewing.findUnique({
    where: { id: viewingId },
    include: {
      property: true,
      broker: { select: { id: true, nameEn: true, nameAr: true, phone: true } },
    },
  });

  if (!viewing) {
    return { sent: false, reason: 'Viewing not found' };
  }

  // Duplicate prevention
  if (viewing.confirmationSentAt) {
    return {
      sent: false,
      reason: `Confirmation already sent at ${viewing.confirmationSentAt.toISOString()}`,
    };
  }

  // Log what would be sent
  log.info('Sending viewing confirmation', {
    viewingId,
    clientName: viewing.clientName,
    clientPhone: viewing.clientPhone,
    propertyTitle: viewing.property.titleEn,
    viewingDate: viewing.viewingDate.toISOString(),
    viewingTime: viewing.viewingTime,
  });

  // Build WhatsApp message
  const brokerLine = viewing.broker
    ? `\u{1F464} Broker: ${viewing.broker.nameEn} (${viewing.broker.phone})`
    : '';
  const location = [viewing.property.district, viewing.property.city]
    .filter(Boolean)
    .join(', ');

  const message = [
    '\u2705 Viewing Confirmed',
    '',
    `\u{1F4CD} ${viewing.property.titleEn}`,
    `\u{1F4C5} ${formatViewingDate(viewing.viewingDate)}`,
    `\u{1F550} ${viewing.viewingTime}`,
    brokerLine,
    '',
    location,
    '',
    'Please arrive 5 minutes early. If you need to reschedule, contact us.',
  ]
    .filter((line) => line !== '')
    .join('\n');

  // Send via WhatsApp (best-effort, non-blocking)
  try {
    await whatsappService.sendMessage(viewing.userId, viewing.clientPhone, message);
  } catch (err) {
    log.warn('WhatsApp send failed for viewing confirmation', {
      viewingId,
      clientPhone: viewing.clientPhone,
      error: err instanceof Error ? err.message : String(err),
    });
  }

  // Mark as sent
  await prisma.propertyViewing.update({
    where: { id: viewingId },
    data: { confirmationSentAt: new Date() },
  });

  return { sent: true };
}

/**
 * Send a viewing reminder notification to the client.
 *
 * Supports two types: 24-hour and 2-hour reminders.
 * Checks the corresponding sentAt field to prevent duplicate sends.
 *
 * @param viewingId - PropertyViewing record ID
 * @param type - Reminder type: '24h' or '2h'
 * @returns Whether the notification was sent
 */
export async function sendViewingReminder(
  viewingId: string,
  type: '24h' | '2h',
): Promise<NotificationResult> {
  const viewing = await prisma.propertyViewing.findUnique({
    where: { id: viewingId },
    include: {
      property: true,
      broker: { select: { id: true, nameEn: true, nameAr: true, phone: true } },
    },
  });

  if (!viewing) {
    return { sent: false, reason: 'Viewing not found' };
  }

  // Duplicate prevention based on reminder type
  if (type === '24h' && viewing.reminder24hSentAt) {
    return {
      sent: false,
      reason: `24h reminder already sent at ${viewing.reminder24hSentAt.toISOString()}`,
    };
  }

  if (type === '2h' && viewing.reminder2hSentAt) {
    return {
      sent: false,
      reason: `2h reminder already sent at ${viewing.reminder2hSentAt.toISOString()}`,
    };
  }

  // Log what would be sent
  log.info(`Sending ${type} viewing reminder`, {
    viewingId,
    type,
    clientName: viewing.clientName,
    clientPhone: viewing.clientPhone,
    propertyTitle: viewing.property.titleEn,
    viewingDate: viewing.viewingDate.toISOString(),
    viewingTime: viewing.viewingTime,
  });

  // Build WhatsApp message based on reminder type
  const location = [viewing.property.district, viewing.property.city]
    .filter(Boolean)
    .join(', ');

  let message: string;

  if (type === '24h') {
    const brokerName = viewing.broker?.nameEn ?? 'your broker';
    const brokerPhone = viewing.broker?.phone ?? '';
    message = [
      '\u23F0 Viewing Reminder - Tomorrow',
      '',
      `\u{1F4CD} ${viewing.property.titleEn}`,
      `\u{1F4C5} Tomorrow at ${viewing.viewingTime}`,
      `\u{1F464} Your broker: ${brokerName}`,
      brokerPhone ? `\u{1F4F1} Broker phone: ${brokerPhone}` : '',
      '',
      location,
    ]
      .filter((line) => line !== '')
      .join('\n');
  } else {
    const brokerInfo = viewing.broker
      ? `${viewing.broker.nameEn}: ${viewing.broker.phone}`
      : 'your broker';
    message = [
      '\u23F0 Your viewing is in 2 hours',
      '',
      `\u{1F4CD} ${viewing.property.titleEn}`,
      `\u{1F550} ${viewing.viewingTime}`,
      `\u{1F464} ${brokerInfo}`,
      '',
      'See you soon!',
    ].join('\n');
  }

  // Send via WhatsApp (best-effort, non-blocking)
  try {
    await whatsappService.sendMessage(viewing.userId, viewing.clientPhone, message);
  } catch (err) {
    log.warn(`WhatsApp send failed for ${type} viewing reminder`, {
      viewingId,
      clientPhone: viewing.clientPhone,
      error: err instanceof Error ? err.message : String(err),
    });
  }

  // Mark the appropriate reminder as sent
  const updateData =
    type === '24h'
      ? { reminder24hSentAt: new Date() }
      : { reminder2hSentAt: new Date() };

  await prisma.propertyViewing.update({
    where: { id: viewingId },
    data: updateData,
  });

  return { sent: true };
}

/**
 * Send a post-viewing follow-up notification to the client.
 *
 * Checks followUpSentAt to prevent duplicate sends.
 *
 * @param viewingId - PropertyViewing record ID
 * @returns Whether the notification was sent
 */
export async function sendPostViewingFollowUp(
  viewingId: string,
): Promise<NotificationResult> {
  const viewing = await prisma.propertyViewing.findUnique({
    where: { id: viewingId },
    include: {
      property: true,
      broker: { select: { id: true, nameEn: true, nameAr: true, phone: true } },
    },
  });

  if (!viewing) {
    return { sent: false, reason: 'Viewing not found' };
  }

  // Duplicate prevention
  if (viewing.followUpSentAt) {
    return {
      sent: false,
      reason: `Follow-up already sent at ${viewing.followUpSentAt.toISOString()}`,
    };
  }

  // Log what would be sent
  log.info('Sending post-viewing follow-up', {
    viewingId,
    clientName: viewing.clientName,
    clientPhone: viewing.clientPhone,
    propertyTitle: viewing.property.titleEn,
    interestLevel: viewing.interestLevel,
  });

  // Build WhatsApp message
  const message = [
    `Thank you for visiting ${viewing.property.titleEn}!`,
    '',
    "We'd love to hear your thoughts. Would you like to:",
    '\u2022 Schedule a second viewing',
    '\u2022 Make an offer',
    '\u2022 See similar properties',
    '',
    'Reply to let us know!',
  ].join('\n');

  // Send via WhatsApp (best-effort, non-blocking)
  try {
    await whatsappService.sendMessage(viewing.userId, viewing.clientPhone, message);
  } catch (err) {
    log.warn('WhatsApp send failed for post-viewing follow-up', {
      viewingId,
      clientPhone: viewing.clientPhone,
      error: err instanceof Error ? err.message : String(err),
    });
  }

  // Mark as sent
  await prisma.propertyViewing.update({
    where: { id: viewingId },
    data: { followUpSentAt: new Date() },
  });

  return { sent: true };
}

// ── Match notifications ──────────────────────────────────────────────────────

/**
 * Notify a client about new matching properties.
 *
 * Logs what would be sent via WhatsApp/email. Does not have a sentAt field
 * on the requirement itself, so deduplication relies on the caller.
 *
 * @param requirementId - ClientRequirements record ID
 * @param propertyIds - Array of matching property IDs to include in notification
 * @returns Whether the notification was sent
 */
export async function sendNewMatchNotification(
  requirementId: string,
  propertyIds: string[],
): Promise<NotificationResult> {
  if (propertyIds.length === 0) {
    return { sent: false, reason: 'No property IDs provided' };
  }

  const requirement = await prisma.clientRequirements.findUnique({
    where: { id: requirementId },
  });

  if (!requirement) {
    return { sent: false, reason: 'Client requirements not found' };
  }

  const properties = await prisma.property.findMany({
    where: { id: { in: propertyIds } },
    select: {
      id: true,
      titleEn: true,
      titleAr: true,
      city: true,
      district: true,
      bedrooms: true,
      bathrooms: true,
      builtUpArea: true,
      price: true,
      currency: true,
    },
  });

  // Log what would be sent
  log.info('Sending new match notification', {
    requirementId,
    clientName: requirement.clientName,
    clientPhone: requirement.clientPhone,
    clientEmail: requirement.clientEmail,
    matchCount: properties.length,
    propertyIds,
  });

  // Build WhatsApp message using formatPropertyCard for each property
  const propertyCards = properties
    .map((p) => formatPropertyCard(p, 'en'))
    .join('\n\n');

  const message = [
    `\u{1F3E0} ${properties.length} new ${properties.length === 1 ? 'property matches' : 'properties match'} your search!`,
    '',
    propertyCards,
    '',
    'Reply VIEW to schedule a viewing for any of these.',
  ].join('\n');

  // Send via WhatsApp (best-effort, non-blocking)
  // Use the requirement's userId (org owner) for WhatsApp credentials
  try {
    await whatsappService.sendMessage(
      requirement.userId,
      requirement.clientPhone,
      message,
    );
  } catch (err) {
    log.warn('WhatsApp send failed for new match notification', {
      requirementId,
      clientPhone: requirement.clientPhone,
      error: err instanceof Error ? err.message : String(err),
    });
  }

  // Update sentAt on each PropertyMatch record
  await prisma.propertyMatch.updateMany({
    where: {
      requirementId,
      propertyId: { in: propertyIds },
      sentAt: null,
    },
    data: { sentAt: new Date() },
  });

  return { sent: true };
}

// ── Price / status alerts ────────────────────────────────────────────────────

/**
 * Alert interested clients when a property's price drops.
 *
 * Finds all PropertyMatch records with status 'interested' or 'viewed'
 * for the given property and logs the alert details.
 *
 * @param propertyId - Property whose price dropped
 * @param oldPrice - Previous price
 * @param newPrice - New (lower) price
 * @returns Whether the notification was sent
 */
export async function sendPriceDropAlert(
  propertyId: string,
  oldPrice: number,
  newPrice: number,
): Promise<NotificationResult> {
  const property = await prisma.property.findUnique({
    where: { id: propertyId },
    select: {
      id: true,
      userId: true,
      titleEn: true,
      titleAr: true,
      city: true,
      district: true,
      currency: true,
    },
  });

  if (!property) {
    return { sent: false, reason: 'Property not found' };
  }

  // Find all interested/viewed clients for this property
  const interestedMatches = await prisma.propertyMatch.findMany({
    where: {
      propertyId,
      status: { in: ['interested', 'viewed', 'sent'] },
    },
    include: {
      requirement: {
        select: {
          id: true,
          clientName: true,
          clientPhone: true,
          clientEmail: true,
        },
      },
    },
  });

  if (interestedMatches.length === 0) {
    return { sent: false, reason: 'No interested clients to notify' };
  }

  const dropPercentage = Math.round(((oldPrice - newPrice) / oldPrice) * 100);
  const currency = property.currency ?? 'QAR';
  const location = [property.district, property.city]
    .filter(Boolean)
    .join(', ');

  // Build the price drop message template
  const message = [
    '\u{1F4B0} Price Drop Alert!',
    '',
    property.titleEn,
    `\u{1F4CD} ${location}`,
    `Was: ${currency} ${oldPrice.toLocaleString()}`,
    `Now: ${currency} ${newPrice.toLocaleString()} (${dropPercentage}% off)`,
    '',
    'Interested? Reply to schedule a viewing.',
  ].join('\n');

  // Send to each interested client
  for (const match of interestedMatches) {
    log.info('Sending price drop alert', {
      propertyId,
      propertyTitle: property.titleEn,
      clientName: match.requirement.clientName,
      clientPhone: match.requirement.clientPhone,
      oldPrice,
      newPrice,
      currency,
      dropPercentage,
    });

    // Send via WhatsApp (best-effort, non-blocking)
    try {
      await whatsappService.sendMessage(
        property.userId,
        match.requirement.clientPhone,
        message,
      );
    } catch (err) {
      log.warn('WhatsApp send failed for price drop alert', {
        propertyId,
        clientPhone: match.requirement.clientPhone,
        error: err instanceof Error ? err.message : String(err),
      });
    }
  }

  return { sent: true };
}

/**
 * Alert all interested clients when a property's status changes.
 *
 * Relevant for transitions like AVAILABLE -> UNDER_OFFER, SOLD, etc.
 *
 * @param propertyId - Property whose status changed
 * @param newStatus - The new property status
 * @returns Whether the notification was sent
 */
export async function sendStatusChangeAlert(
  propertyId: string,
  newStatus: string,
): Promise<NotificationResult> {
  const property = await prisma.property.findUnique({
    where: { id: propertyId },
    select: {
      id: true,
      userId: true,
      titleEn: true,
      titleAr: true,
      city: true,
      district: true,
      currency: true,
      price: true,
    },
  });

  if (!property) {
    return { sent: false, reason: 'Property not found' };
  }

  // Find all clients who have shown interest
  const interestedMatches = await prisma.propertyMatch.findMany({
    where: {
      propertyId,
      status: { in: ['interested', 'viewed', 'sent', 'new'] },
    },
    include: {
      requirement: {
        select: {
          id: true,
          clientName: true,
          clientPhone: true,
          clientEmail: true,
        },
      },
    },
  });

  if (interestedMatches.length === 0) {
    return { sent: false, reason: 'No interested clients to notify' };
  }

  // Build status-specific call-to-action
  const statusUpper = newStatus.toUpperCase();
  let callToAction = '';
  if (statusUpper === 'SOLD' || statusUpper === 'RENTED') {
    callToAction =
      'We can find similar properties for you. Reply SEARCH to see alternatives.';
  } else if (statusUpper === 'UNDER_OFFER') {
    callToAction = 'Act fast! Reply to express your interest.';
  }

  const message = [
    '\u{1F4CB} Property Update',
    '',
    `${property.titleEn} is now ${newStatus}.`,
    callToAction ? '' : null,
    callToAction || null,
  ]
    .filter((line): line is string => line !== null)
    .join('\n');

  // Send to each interested client
  for (const match of interestedMatches) {
    log.info('Sending status change alert', {
      propertyId,
      propertyTitle: property.titleEn,
      newStatus,
      clientName: match.requirement.clientName,
      clientPhone: match.requirement.clientPhone,
    });

    // Send via WhatsApp (best-effort, non-blocking)
    try {
      await whatsappService.sendMessage(
        property.userId,
        match.requirement.clientPhone,
        message,
      );
    } catch (err) {
      log.warn('WhatsApp send failed for status change alert', {
        propertyId,
        clientPhone: match.requirement.clientPhone,
        error: err instanceof Error ? err.message : String(err),
      });
    }
  }

  return { sent: true };
}

// ── Formatting ───────────────────────────────────────────────────────────────

/**
 * Format a property into a WhatsApp-friendly card message.
 *
 * Supports English and Arabic formatting.
 *
 * @param property - Property data object (any shape, expects standard fields)
 * @param language - 'en' for English, 'ar' for Arabic
 * @returns Formatted string suitable for WhatsApp message
 */
export function formatPropertyCard(
  property: {
    titleEn?: string | null;
    titleAr?: string | null;
    district?: string | null;
    city?: string | null;
    bedrooms?: number | null;
    bathrooms?: number | null;
    builtUpArea?: number | null;
    price?: number | null;
    currency?: string | null;
  },
  language: 'en' | 'ar',
): string {
  if (language === 'ar') {
    const title = property.titleAr || property.titleEn || '';
    const location = [property.district, property.city]
      .filter(Boolean)
      .join('\u060C '); // Arabic comma
    const bedrooms = property.bedrooms ?? 0;
    const bathrooms = property.bathrooms ?? 0;
    const area = property.builtUpArea ?? 0;
    const price = property.price ?? 0;
    const currency = property.currency ?? 'QAR';

    return [
      `\u{1F3E0} *${title}*`,
      `\u{1F4CD} ${location}`,
      `\u{1F6CF} ${bedrooms} \u063A\u0631\u0641 | \u{1F6C1} ${bathrooms} \u062D\u0645\u0627\u0645 | \u{1F4D0} ${area} \u0645\u00B2`,
      `\u{1F4B0} ${price} ${currency}`,
    ].join('\n');
  }

  // English (default)
  const title = property.titleEn || '';
  const location = [property.district, property.city]
    .filter(Boolean)
    .join(', ');
  const bedrooms = property.bedrooms ?? 0;
  const bathrooms = property.bathrooms ?? 0;
  const area = property.builtUpArea ?? 0;
  const price = property.price ?? 0;
  const currency = property.currency ?? 'QAR';

  return [
    `\u{1F3E0} *${title}*`,
    `\u{1F4CD} ${location}`,
    `\u{1F6CF} ${bedrooms} BR | \u{1F6C1} ${bathrooms} BA | \u{1F4D0} ${area} sqm`,
    `\u{1F4B0} ${currency} ${price}`,
  ].join('\n');
}
