import {
  EMPTY_JOB_REACTION_COUNTS,
  JOB_REACTION_TYPES,
  JobReactionSummary,
  JobReactionType,
  JobReactionUpdatePayload,
} from '@ehandyhire/shared';
import prisma from '../lib/prisma';
import { AppError } from '../lib/utils';
import { createAndEmitNotification } from './notification.service';

const REACTION_VERBS: Record<JobReactionType, string> = {
  LIKE: 'liked',
  LOVE: 'loved',
  ANGRY: 'reacted angrily to',
};

function emptyCounts() {
  return { ...EMPTY_JOB_REACTION_COUNTS };
}

export async function getCountsForJob(jobId: string) {
  const grouped = await prisma.jobReaction.groupBy({
    by: ['type'],
    where: { jobId },
    _count: { _all: true },
  });

  const counts = emptyCounts();
  for (const row of grouped) {
    counts[row.type as JobReactionType] = row._count._all;
  }
  return counts;
}

export async function getJobReactionSummaries(
  jobIds: string[],
  userId?: string
): Promise<Map<string, JobReactionSummary>> {
  const summaries = new Map<string, JobReactionSummary>();
  if (jobIds.length === 0) return summaries;

  for (const jobId of jobIds) {
    summaries.set(jobId, { counts: emptyCounts(), userReaction: null });
  }

  const grouped = await prisma.jobReaction.groupBy({
    by: ['jobId', 'type'],
    where: { jobId: { in: jobIds } },
    _count: { _all: true },
  });

  for (const row of grouped) {
    const summary = summaries.get(row.jobId);
    if (!summary) continue;
    summary.counts[row.type as JobReactionType] = row._count._all;
  }

  if (userId) {
    const userReactions = await prisma.jobReaction.findMany({
      where: { jobId: { in: jobIds }, userId },
      select: { jobId: true, type: true },
    });
    for (const reaction of userReactions) {
      const summary = summaries.get(reaction.jobId);
      if (summary) summary.userReaction = reaction.type as JobReactionType;
    }
  }

  return summaries;
}

export async function setJobReaction(
  jobId: string,
  userId: string,
  type: JobReactionType
): Promise<JobReactionUpdatePayload> {
  if (!JOB_REACTION_TYPES.includes(type)) {
    throw new AppError('Invalid reaction type', 400);
  }

  const job = await prisma.job.findFirst({
    where: { id: jobId, deletedAt: null, customerId: { not: null } },
    select: { id: true, title: true, customerId: true },
  });
  if (!job) throw new AppError('Job not found', 404);

  const existing = await prisma.jobReaction.findUnique({
    where: { jobId_userId: { jobId, userId } },
  });

  let userReaction: JobReactionType | null;

  if (existing?.type === type) {
    await prisma.jobReaction.delete({ where: { id: existing.id } });
    userReaction = null;
  } else if (existing) {
    await prisma.jobReaction.update({
      where: { id: existing.id },
      data: { type },
    });
    userReaction = type;
  } else {
    await prisma.jobReaction.create({
      data: { jobId, userId, type },
    });
    userReaction = type;
  }

  const counts = await getCountsForJob(jobId);

  if (userReaction && job.customerId && job.customerId !== userId) {
    const worker = await prisma.user.findUnique({
      where: { id: userId },
      select: { firstName: true, lastName: true },
    });
    const workerName = worker
      ? `${worker.firstName} ${worker.lastName}`.trim()
      : 'A worker';

    await createAndEmitNotification(
      job.customerId,
      'JOB_REACTION',
      'New reaction on your job',
      `${workerName} ${REACTION_VERBS[userReaction]} "${job.title}"`,
      { jobId, reactionType: userReaction, workerUserId: userId }
    );
  }

  return { jobId, userId, userReaction, counts };
}
