import { prisma } from '../config/database';
import { redis } from '../config/redis';
import { env } from '../config/env';
import { logger } from '../utils/logger';
import { emailService } from './email.service';
import { feedService, type FeedItem } from '../modules/social/feed.service';
import { notificationService } from '../modules/social/notification.service';
import { friendshipService } from '../modules/social/friendship.service';
import * as fs from 'fs';
import * as path from 'path';

const REDIS_PREFIX = 'social:digest:last:';

export const socialDigestService = {
  /** Scan for users needing a digest and send emails */
  async scan() {
    // Check if social is enabled
    const settings = await prisma.siteSettings.findFirst({
      select: { enableSocial: true, organizationName: true },
    });
    if (!settings?.enableSocial) return;

    const now = new Date();
    const dayOfWeek = now.getDay(); // 0=Sunday

    // Find users opted into digests
    const prefs = await prisma.notificationPreferences.findMany({
      where: {
        digestFrequency: { in: ['daily', 'weekly'] },
        emailNotifications: true,
      },
      include: {
        user: { select: { id: true, name: true, email: true } },
      },
    });

    let sent = 0;
    for (const pref of prefs) {
      try {
        // Weekly digests only on Mondays
        if (pref.digestFrequency === 'weekly' && dayOfWeek !== 1) continue;

        // Check Redis cooldown (prevent double-sends)
        const cooldownKey = `${REDIS_PREFIX}${pref.userId}`;
        const lastSent = await redis.get(cooldownKey);
        if (lastSent) continue;

        const shouldSend = await this.shouldSendDigest(pref.userId, pref.digestFrequency);
        if (!shouldSend) continue;

        await this.sendDigest(
          pref.userId,
          pref.user.email,
          pref.user.name || pref.user.email,
          pref.digestFrequency,
          settings.organizationName || 'Changemaker',
          env.DOMAIN,
        );

        // Set cooldown: daily=20h, weekly=6d (prevents duplicates even with timing variance)
        const cooldownSeconds = pref.digestFrequency === 'daily' ? 72000 : 518400;
        await redis.set(cooldownKey, now.toISOString(), 'EX', cooldownSeconds);

        // Update lastDigestSentAt
        await prisma.notificationPreferences.update({
          where: { userId: pref.userId },
          data: { lastDigestSentAt: now },
        });

        sent++;
      } catch (err) {
        logger.error(`Failed to send social digest to ${pref.user.email}`, { error: err });
      }
    }

    if (sent > 0) {
      logger.info(`Social digest: sent ${sent} emails`);
    }
  },

  /** Determine if a user should receive a digest based on timing */
  async shouldSendDigest(userId: string, frequency: string): Promise<boolean> {
    const pref = await prisma.notificationPreferences.findUnique({
      where: { userId },
      select: { lastDigestSentAt: true },
    });

    if (!pref?.lastDigestSentAt) return true; // Never sent before

    const hoursSinceLastDigest = (Date.now() - pref.lastDigestSentAt.getTime()) / (1000 * 60 * 60);

    if (frequency === 'daily') return hoursSinceLastDigest >= 20;
    if (frequency === 'weekly') return hoursSinceLastDigest >= 144; // 6 days

    return false;
  },

  /** Generate and send a digest email */
  async sendDigest(
    userId: string,
    email: string,
    name: string,
    frequency: string,
    orgName: string,
    domain: string,
  ) {
    // Calculate "since" based on frequency
    const since = new Date();
    if (frequency === 'daily') {
      since.setDate(since.getDate() - 1);
    } else {
      since.setDate(since.getDate() - 7);
    }

    // Gather data
    const [friendFeedResult, unreadCount, pendingReceived] = await Promise.all([
      feedService.getFriendFeed(userId, 1, 10),
      notificationService.getUnreadCount(userId),
      friendshipService.listPendingReceived(userId),
    ]);

    const activities: FeedItem[] = friendFeedResult.items;
    const pendingCount = pendingReceived.length;

    // Skip digest if nothing to report
    if (activities.length === 0 && unreadCount === 0 && pendingCount === 0) return;

    // Build HTML sections
    const activitySection = activities.length > 0
      ? `<div class="section-title">Friend Activity</div>${activities.slice(0, 5).map((a) =>
          `<div class="activity-item">
            <span class="activity-name">${escapeHtml(a.userName || a.userEmail)}</span>
            <span class="activity-desc">${escapeHtml(a.title)}</span>
          </div>`,
        ).join('')}${activities.length > 5 ? `<p style="color:#999; font-size:13px;">...and ${activities.length - 5} more</p>` : ''}`
      : '';

    const pendingSection = pendingCount > 0
      ? `<div class="section-title">Pending Friend Requests</div><p>You have ${pendingCount} pending friend request${pendingCount > 1 ? 's' : ''} waiting for your response.</p>`
      : '';

    // Build text versions
    const activityText = activities.length > 0
      ? `Recent friend activity:\n${activities.slice(0, 5).map((a) => `  - ${a.userName || a.userEmail}: ${a.title}`).join('\n')}`
      : '';

    const pendingText = pendingCount > 0
      ? `You have ${pendingCount} pending friend request${pendingCount > 1 ? 's' : ''}.`
      : '';

    const appUrl = `https://app.${domain}`;
    const feedUrl = `${appUrl}/volunteer/feed`;
    const settingsUrl = `${appUrl}/volunteer/notifications`;
    const digestPeriod = frequency === 'daily' ? 'Daily' : 'Weekly';

    // Load templates
    let html: string;
    let text: string;

    try {
      const templateDir = path.join(__dirname, '../templates/email');
      html = fs.readFileSync(path.join(templateDir, 'social-digest.html'), 'utf-8');
      text = fs.readFileSync(path.join(templateDir, 'social-digest.txt'), 'utf-8');
    } catch {
      logger.warn('Social digest templates not found, using fallback');
      html = `<p>Hi ${escapeHtml(name)}, you have ${unreadCount} unread notifications and ${activities.length} friend activities. <a href="${feedUrl}">View feed</a></p>`;
      text = `Hi ${name}, you have ${unreadCount} unread notifications and ${activities.length} friend activities. View feed: ${feedUrl}`;
    }

    // Replace placeholders
    const replacements: Record<string, string> = {
      '{{ORGANIZATION_NAME}}': escapeHtml(orgName),
      '{{DIGEST_PERIOD}}': digestPeriod,
      '{{USER_NAME}}': escapeHtml(name),
      '{{UNREAD_NOTIFICATIONS}}': String(unreadCount),
      '{{PENDING_REQUESTS}}': String(pendingCount),
      '{{FRIEND_ACTIVITY_COUNT}}': String(activities.length),
      '{{FRIEND_ACTIVITY_SECTION}}': activitySection,
      '{{PENDING_REQUESTS_SECTION}}': pendingSection,
      '{{FRIEND_ACTIVITY_TEXT}}': activityText,
      '{{PENDING_REQUESTS_TEXT}}': pendingText,
      '{{FEED_URL}}': feedUrl,
      '{{SETTINGS_URL}}': settingsUrl,
    };

    for (const [key, value] of Object.entries(replacements)) {
      html = html.replace(new RegExp(escapeRegExp(key), 'g'), value);
      text = text.replace(new RegExp(escapeRegExp(key), 'g'), value);
    }

    await emailService.sendEmail({
      to: email,
      subject: `${digestPeriod} Social Digest — ${orgName}`,
      html,
      text,
    });
  },
};

function escapeHtml(str: string): string {
  return str
    .replace(/&/g, '&amp;')
    .replace(/</g, '&lt;')
    .replace(/>/g, '&gt;')
    .replace(/"/g, '&quot;')
    .replace(/'/g, '&#039;');
}

function escapeRegExp(str: string): string {
  return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}
