import { prisma } from '../../config/database';
import { redis } from '../../config/redis';
import { notificationService } from './notification.service';
import { friendshipService } from './friendship.service';
import { sseService } from './sse.service';

const POKE_COOLDOWN_SECONDS = 24 * 60 * 60; // 24 hours

export const pokeService = {
  /** Send a poke to a friend (1 per pair per 24h via Redis) */
  async sendPoke(fromUserId: string, toUserId: string) {
    if (fromUserId === toUserId) {
      throw Object.assign(new Error('Cannot poke yourself'), { statusCode: 400 });
    }

    // Must be accepted friends
    const friendIds = await friendshipService.getFriendIds(fromUserId);
    if (!friendIds.includes(toUserId)) {
      throw Object.assign(new Error('You can only poke friends'), { statusCode: 403 });
    }

    // Check block
    const blocked = await friendshipService.isBlocked(fromUserId, toUserId);
    if (blocked) {
      throw Object.assign(new Error('Cannot poke this user'), { statusCode: 403 });
    }

    // Check 24h cooldown via Redis
    const cooldownKey = `social:poke:${fromUserId}:${toUserId}`;
    const existing = await redis.get(cooldownKey);
    if (existing) {
      const ttl = await redis.ttl(cooldownKey);
      const hoursLeft = Math.ceil(ttl / 3600);
      throw Object.assign(
        new Error(`You can poke this person again in ~${hoursLeft}h`),
        { statusCode: 429 },
      );
    }

    // Create poke record
    const poke = await prisma.poke.create({
      data: { fromUserId, toUserId },
      include: {
        from: { select: { id: true, name: true, email: true } },
        to: { select: { id: true, name: true, email: true } },
      },
    });

    // Set 24h cooldown
    await redis.set(cooldownKey, '1', 'EX', POKE_COOLDOWN_SECONDS);

    // Send notification (fire-and-forget)
    const senderName = poke.from.name || poke.from.email;
    notificationService.createNotification(
      toUserId,
      'poke',
      'You got poked!',
      `${senderName} poked you`,
      { pokeId: poke.id, fromUserId },
    ).catch(() => {});

    // Push real-time SSE event
    sseService.sendToUser(toUserId, 'poke', {
      pokeId: poke.id,
      from: poke.from,
    });

    return poke;
  },

  /** List received pokes (paginated) */
  async listReceivedPokes(userId: string, page: number, limit: number) {
    const skip = (page - 1) * limit;

    const [pokes, total] = await Promise.all([
      prisma.poke.findMany({
        where: { toUserId: userId },
        include: {
          from: { select: { id: true, name: true, email: true } },
        },
        orderBy: { createdAt: 'desc' },
        skip,
        take: limit,
      }),
      prisma.poke.count({ where: { toUserId: userId } }),
    ]);

    return {
      pokes,
      pagination: { page, limit, total, totalPages: Math.ceil(total / limit) },
    };
  },

  /** Mark a poke as read */
  async markPokeRead(userId: string, pokeId: number) {
    const poke = await prisma.poke.findFirst({
      where: { id: pokeId, toUserId: userId },
    });
    if (!poke) {
      throw Object.assign(new Error('Poke not found'), { statusCode: 404 });
    }

    return prisma.poke.update({
      where: { id: pokeId },
      data: { isRead: true },
    });
  },

  /** Get unread poke count */
  async getUnreadPokeCount(userId: string): Promise<number> {
    return prisma.poke.count({
      where: { toUserId: userId, isRead: false },
    });
  },

  /** Check if current user can poke target (cooldown remaining) */
  async getPokeCooldown(fromUserId: string, toUserId: string): Promise<number | null> {
    const cooldownKey = `social:poke:${fromUserId}:${toUserId}`;
    const ttl = await redis.ttl(cooldownKey);
    return ttl > 0 ? ttl : null;
  },
};
