import { prisma } from '../../config/database';
import { friendshipService } from './friendship.service';
import { sseService } from './sse.service';
import { logger } from '../../utils/logger';

const OFFLINE_TIMEOUT_MS = 5 * 60 * 1000; // 5 minutes

export const presenceService = {
  /** Set a user as online (upsert presence) */
  async setOnline(userId: string) {
    await prisma.userPresence.upsert({
      where: { userId },
      update: { isOnline: true, lastActivityAt: new Date() },
      create: { userId, isOnline: true, lastActivityAt: new Date() },
    });

    // Notify friends that this user came online
    this.broadcastPresence(userId, true).catch(() => {});
  },

  /** Set a user as offline */
  async setOffline(userId: string) {
    await prisma.userPresence.upsert({
      where: { userId },
      update: { isOnline: false, lastActivityAt: new Date() },
      create: { userId, isOnline: false, lastActivityAt: new Date() },
    });

    // Notify friends that this user went offline
    this.broadcastPresence(userId, false).catch(() => {});
  },

  /** Update heartbeat (keep alive) */
  async heartbeat(userId: string) {
    await prisma.userPresence.upsert({
      where: { userId },
      update: { isOnline: true, lastActivityAt: new Date() },
      create: { userId, isOnline: true, lastActivityAt: new Date() },
    });
  },

  /** Get online friends for a user */
  async getOnlineFriends(userId: string) {
    const friendIds = await friendshipService.getFriendIds(userId);
    if (friendIds.length === 0) return [];

    // Filter by privacy: only show friends who allow online status visibility
    const hiddenIds = new Set(
      (await prisma.privacySettings.findMany({
        where: {
          userId: { in: friendIds },
          showOnlineStatus: false,
        },
        select: { userId: true },
      })).map((p) => p.userId),
    );

    const visibleIds = friendIds.filter((id) => !hiddenIds.has(id));
    if (visibleIds.length === 0) return [];

    const online = await prisma.userPresence.findMany({
      where: {
        userId: { in: visibleIds },
        isOnline: true,
      },
      include: {
        user: { select: { id: true, name: true, email: true } },
      },
    });

    return online.map((p) => ({
      userId: p.userId,
      name: p.user.name,
      email: p.user.email,
      lastActivityAt: p.lastActivityAt,
    }));
  },

  /** Broadcast presence change to a user's friends via SSE */
  async broadcastPresence(userId: string, isOnline: boolean) {
    // Check if user allows online status visibility
    const privacy = await prisma.privacySettings.findUnique({ where: { userId } });
    if (privacy?.showOnlineStatus === false) return;

    const friendIds = await friendshipService.getFriendIds(userId);
    const user = await prisma.user.findUnique({
      where: { id: userId },
      select: { name: true, email: true },
    });

    sseService.sendToUsers(friendIds, 'presence', {
      userId,
      name: user?.name || null,
      email: user?.email || '',
      isOnline,
    });
  },

  /** Cleanup: mark stale users as offline (run on startup + interval) */
  async cleanupStale() {
    const cutoff = new Date(Date.now() - OFFLINE_TIMEOUT_MS);
    const stale = await prisma.userPresence.findMany({
      where: {
        isOnline: true,
        lastActivityAt: { lt: cutoff },
      },
      select: { userId: true },
    });

    if (stale.length > 0) {
      await prisma.userPresence.updateMany({
        where: {
          userId: { in: stale.map((s) => s.userId) },
        },
        data: { isOnline: false },
      });
      logger.info(`Presence cleanup: marked ${stale.length} stale users offline`);
    }
  },

  /** Mark all users offline (startup cleanup) */
  async markAllOffline() {
    const { count } = await prisma.userPresence.updateMany({
      where: { isOnline: true },
      data: { isOnline: false },
    });
    if (count > 0) {
      logger.info(`Presence startup: marked ${count} users offline`);
    }
  },
};
