import { Injectable, NotFoundException, ForbiddenException } from '@nestjs/common';
import { PrismaService } from '../../prisma/prisma.service';
import { NotificationsService } from '../notifications/notifications.service';
import { CreateFeedDto, CreateCommentDto, ReactDto, CreatePollDto } from './dto/feed.dto';

@Injectable()
export class FeedsService {
  constructor(
    private prisma: PrismaService,
    private notifications: NotificationsService,
  ) {}

  async findAll(
    userId: string,
    page: any = 1,
    limit: any = 20,
    contentType?: string,
    search?: string,
  ) {
    const pageNum = Math.max(1, parseInt(page) || 1);
    const limitNum = Math.min(100, parseInt(limit) || 20);

    const where: any = {};
    if (contentType && contentType !== 'ALL') where.contentType = contentType;
    if (search) {
      where.OR = [
        { body: { contains: search } },
        { author: { name: { contains: search } } },
      ];
    }

    const include = {
      author: { select: { id: true, name: true, profilePicture: true, role: true } },
      // Only fetch the requesting user's own reaction — O(1) per post, not O(reactions)
      reactions: { where: { userId }, select: { reactionType: true }, take: 1 },
      _count: { select: { comments: true, reactions: true } },
      poll: {
        include: {
          options: {
            include: {
              _count: { select: { votes: true } },
              votes: { where: { userId }, select: { id: true }, take: 1 },
            },
          },
        },
      },
    };

    const [posts, total] = await Promise.all([
      this.prisma.feed.findMany({
        where,
        include,
        orderBy: [{ pinned: 'desc' }, { createdAt: 'desc' }],
        skip: (pageNum - 1) * limitNum,
        take: limitNum,
      }),
      this.prisma.feed.count({ where }),
    ]);

    return { posts, total, page: pageNum, limit: limitNum };
  }

  async findOne(id: string) {
    const feed = await this.prisma.feed.findUnique({
      where: { id },
      include: {
        author: { select: { id: true, name: true, profilePicture: true, role: true } },
        reactions: true,
        _count: { select: { comments: true, reactions: true } },
        comments: {
          include: { user: { select: { id: true, name: true, profilePicture: true } } },
          orderBy: { createdAt: 'asc' },
        },
        poll: { include: { options: { include: { votes: true, _count: { select: { votes: true } } } } } },
      },
    });
    if (!feed) throw new NotFoundException('Post not found');
    return feed;
  }

  async create(authorId: string, dto: CreateFeedDto) {
    const hasPoll = dto.contentType === 'POLL' && dto.pollQuestion && dto.pollOptions?.length;
    const feed = await this.prisma.feed.create({
      data: {
        authorId,
        contentType: dto.contentType,
        body: dto.body,
        mediaUrls: dto.mediaUrls ?? [],
        ...(hasPoll
          ? {
              poll: {
                create: {
                  question: dto.pollQuestion!,
                  endsAt: dto.pollEndsAt ? new Date(dto.pollEndsAt) : undefined,
                  options: { create: dto.pollOptions!.map((text) => ({ text })) },
                },
              },
            }
          : {}),
      },
    });

    const allUsers = await this.prisma.user.findMany({
      where: { status: 'APPROVED', id: { not: authorId } },
      select: { id: true, fcmToken: true },
    });

    for (const u of allUsers) {
      await this.notifications.create(u.id, {
        type: 'NEW_POST',
        title: 'New post',
        body: dto.body?.slice(0, 80) ?? 'New content posted',
        payload: { feedId: feed.id },
        fcmToken: u.fcmToken,
      });
    }

    return feed;
  }

  async update(id: string, dto: Partial<CreateFeedDto>) {
    const { pollQuestion, pollOptions, pollEndsAt, ...feedData } = dto as any;

    const feed = await this.prisma.feed.update({ where: { id }, data: feedData });

    if (pollQuestion !== undefined || pollOptions !== undefined) {
      const poll = await this.prisma.poll.findUnique({ where: { feedId: id } });
      if (poll) {
        await this.prisma.poll.update({
          where: { feedId: id },
          data: {
            ...(pollQuestion !== undefined ? { question: pollQuestion } : {}),
            ...(pollEndsAt !== undefined ? { endsAt: pollEndsAt ? new Date(pollEndsAt) : null } : {}),
          },
        });
        if (pollOptions?.length) {
          const existing = await this.prisma.pollOption.findMany({
            where: { pollId: poll.id },
            orderBy: { id: 'asc' },
          });
          await Promise.all(
            existing.map((opt, i) =>
              pollOptions[i] !== undefined
                ? this.prisma.pollOption.update({ where: { id: opt.id }, data: { text: pollOptions[i] } })
                : Promise.resolve(),
            ),
          );
        }
      }
    }

    return feed;
  }

  async pin(id: string, pinned: boolean) {
    return this.prisma.feed.update({ where: { id }, data: { pinned } });
  }

  async delete(id: string) {
    return this.prisma.feed.delete({ where: { id } });
  }

  async react(feedId: string, userId: string, dto: ReactDto) {
    return this.prisma.feedReaction.upsert({
      where: { feedId_userId: { feedId, userId } },
      update: { reactionType: dto.reactionType },
      create: { feedId, userId, reactionType: dto.reactionType },
    });
  }

  async unreact(feedId: string, userId: string) {
    return this.prisma.feedReaction.delete({ where: { feedId_userId: { feedId, userId } } });
  }

  async addComment(feedId: string, userId: string, dto: CreateCommentDto) {
    return this.prisma.feedComment.create({
      data: { feedId, userId, body: dto.body },
      include: { user: { select: { id: true, name: true, profilePicture: true } } },
    });
  }

  async deleteComment(id: string, requesterId: string, requesterRole: string) {
    const comment = await this.prisma.feedComment.findUnique({ where: { id } });
    if (!comment) throw new NotFoundException('Comment not found');
    const isAdmin = requesterRole === 'SUPER_ADMIN' || requesterRole === 'MODERATOR';
    if (comment.userId !== requesterId && !isAdmin) {
      throw new ForbiddenException('You can only delete your own comments');
    }
    return this.prisma.feedComment.delete({ where: { id } });
  }

  async votePoll(pollOptionId: string, userId: string) {
    return this.prisma.pollVote.upsert({
      where: { pollOptionId_userId: { pollOptionId, userId } },
      update: {},
      create: { pollOptionId, userId },
    });
  }
}
