import prisma from '../lib/prisma';
import { AppError } from '../lib/utils';
import { createAndEmitNotification } from './notification.service';
import { getWorkerProfileId } from './messages.service';

const commentUserSelect = {
  id: true,
  firstName: true,
  lastName: true,
  avatarUrl: true,
  role: true,
} as const;

export async function assertJobCommentAccess(jobId: string, userId: string, role: string) {
  const job = await prisma.job.findFirst({
    where: { id: jobId, deletedAt: null },
    include: {
      quotations: { select: { workerId: true } },
    },
  });

  if (!job) throw new AppError('Job not found', 404);

  if (role === 'ADMIN') return job;

  if (job.customerId === userId) return job;

  if (role !== 'WORKER') {
    throw new AppError('Not authorized to view comments on this job', 403);
  }

  const workerProfileId = await getWorkerProfileId(userId);
  if (!workerProfileId) {
    throw new AppError('Not authorized to view comments on this job', 403);
  }

  const isAssigned = job.workerId === workerProfileId;
  const isInvited = job.invitedWorkerId === workerProfileId;
  const hasQuote = job.quotations.some((q) => q.workerId === workerProfileId);

  if (isAssigned || isInvited || hasQuote) return job;

  // Open jobs: any worker can ask questions before or without applying
  if (['OPEN', 'QUOTED'].includes(job.status)) return job;

  throw new AppError('Not authorized to view comments on this job', 403);
}

export async function listJobComments(jobId: string) {
  return prisma.jobComment.findMany({
    where: { jobId },
    orderBy: { createdAt: 'asc' },
    include: {
      user: { select: commentUserSelect },
    },
  });
}

export async function createJobComment(
  jobId: string,
  userId: string,
  role: string,
  content: string
) {
  if (!['CUSTOMER', 'WORKER'].includes(role)) {
    throw new AppError('Only customers and workers can comment on jobs', 403);
  }

  const job = await assertJobCommentAccess(jobId, userId, role);

  const comment = await prisma.jobComment.create({
    data: {
      jobId,
      userId,
      content: content.trim(),
    },
    include: {
      user: { select: commentUserSelect },
    },
  });

  await notifyJobComment(job, userId, comment.user, comment.id);

  return comment;
}

async function notifyJobComment(
  job: {
    id: string;
    title: string;
    customerId: string | null;
    workerId: string | null;
    invitedWorkerId: string | null;
    quotations: { workerId: string }[];
  },
  authorUserId: string,
  author: { firstName: string; lastName: string; role: string },
  commentId: string
) {
  const authorName = `${author.firstName} ${author.lastName}`.trim();
  const recipientIds = new Set<string>();

  if (author.role === 'CUSTOMER' && job.customerId === authorUserId) {
    if (job.workerId) {
      const worker = await prisma.workerProfile.findUnique({
        where: { id: job.workerId },
        select: { userId: true },
      });
      if (worker?.userId) recipientIds.add(worker.userId);
    } else if (job.invitedWorkerId) {
      const invited = await prisma.workerProfile.findUnique({
        where: { id: job.invitedWorkerId },
        select: { userId: true },
      });
      if (invited?.userId) recipientIds.add(invited.userId);
    }

    const quotedWorkers = await prisma.workerProfile.findMany({
      where: {
        id: { in: job.quotations.map((q) => q.workerId) },
      },
      select: { userId: true },
    });
    quotedWorkers.forEach((w) => recipientIds.add(w.userId));

    const workerCommenters = await prisma.jobComment.findMany({
      where: {
        jobId: job.id,
        userId: { not: authorUserId },
        user: { role: 'WORKER' },
      },
      select: { userId: true },
      distinct: ['userId'],
    });
    workerCommenters.forEach((c) => recipientIds.add(c.userId));
  } else if (author.role === 'WORKER' && job.customerId) {
    recipientIds.add(job.customerId);
  }

  recipientIds.delete(authorUserId);

  const label = author.role === 'WORKER' ? 'Worker' : 'Customer';

  await Promise.all(
    [...recipientIds].map((recipientId) =>
      createAndEmitNotification(
        recipientId,
        'JOB_STATUS_CHANGED',
        'New job comment',
        `${label} ${authorName} commented on "${job.title}"`,
        { jobId: job.id, commentId }
      )
    )
  );
}
