import { JobStatus } from '@prisma/client';
import prisma from '../lib/prisma';
import { AppError } from '../lib/utils';
import { createAndEmitNotification } from './notification.service';
import { getIO } from '../socket';

export type JobProgressAction = 'start' | 'mark_complete' | 'confirm_complete' | 'cancel' | 'reopen';

export async function getWorkerProfileIdForUser(userId: string) {
  const profile = await prisma.workerProfile.findUnique({
    where: { userId },
    select: { id: true },
  });
  return profile?.id ?? null;
}

async function emitJobStatusUpdate(job: {
  id: string;
  customerId: string | null;
  worker?: { userId: string } | null;
}) {
  try {
    const io = getIO();
    const full = await prisma.job.findFirst({
      where: { id: job.id },
      include: { category: true, worker: { include: { user: true } } },
    });
    if (!full) return;
    if (job.customerId) {
      io.to(`user:${job.customerId}`).emit('job-status-update', full);
    }
    if (job.worker?.userId) {
      io.to(`user:${job.worker.userId}`).emit('job-status-update', full);
    }
  } catch (e) {
    console.error('Socket error:', e);
  }
}

export async function applyJobProgressAction(
  jobId: string,
  userId: string,
  role: string,
  action: JobProgressAction
) {
  const job = await prisma.job.findFirst({
    where: { id: jobId, deletedAt: null },
    include: {
      worker: { select: { id: true, userId: true } },
      customer: { select: { id: true, firstName: true } },
    },
  });

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

  const workerProfileId = await getWorkerProfileIdForUser(userId);
  const isCustomer = job.customerId === userId;
  const isAssignedWorker = workerProfileId != null && job.workerId === workerProfileId;
  const isAdmin = role === 'ADMIN';

  switch (action) {
    case 'start': {
      if (!isAssignedWorker && !isAdmin) {
        throw new AppError('Only the hired worker can start this job', 403);
      }
      if (job.status !== 'ACCEPTED') {
        throw new AppError('Job can only be started after a worker is hired', 400);
      }
      const updated = await prisma.job.update({
        where: { id: job.id },
        data: { status: 'IN_PROGRESS', workerMarkedCompleteAt: null },
        include: { category: true, worker: { include: { user: true } } },
      });
      if (job.customerId) {
        await createAndEmitNotification(
          job.customerId,
          'JOB_STATUS_CHANGED',
          'Work started',
          `Your worker has started "${job.title}"`,
          { jobId: job.id, status: 'IN_PROGRESS' }
        );
      }
      await emitJobStatusUpdate(job);
      return updated;
    }

    case 'mark_complete': {
      if (!isAssignedWorker && !isAdmin) {
        throw new AppError('Only the hired worker can mark work as finished', 403);
      }
      if (job.status !== 'IN_PROGRESS') {
        throw new AppError('Job must be in progress before marking complete', 400);
      }
      const updated = await prisma.job.update({
        where: { id: job.id },
        data: { workerMarkedCompleteAt: new Date() },
        include: { category: true, worker: { include: { user: true } } },
      });
      if (job.customerId) {
        await createAndEmitNotification(
          job.customerId,
          'JOB_STATUS_CHANGED',
          'Work finished — please confirm',
          `Your worker marked "${job.title}" as done. Confirm when you're satisfied.`,
          { jobId: job.id, status: 'IN_PROGRESS', awaitingConfirmation: true }
        );
      }
      await emitJobStatusUpdate(job);
      return updated;
    }

    case 'confirm_complete': {
      if (!isCustomer && !isAdmin) {
        throw new AppError('Only the customer can confirm job completion', 403);
      }
      if (job.status !== 'IN_PROGRESS') {
        throw new AppError('Only in-progress jobs can be confirmed complete', 400);
      }
      const updated = await prisma.job.update({
        where: { id: job.id },
        data: {
          status: 'COMPLETED',
          completedAt: new Date(),
        },
        include: { category: true, worker: { include: { user: true } } },
      });
      if (job.workerId) {
        await prisma.workerProfile.update({
          where: { id: job.workerId },
          data: { totalJobs: { increment: 1 } },
        });
      }
      if (job.worker?.userId) {
        await createAndEmitNotification(
          job.worker.userId,
          'JOB_STATUS_CHANGED',
          'Job completed',
          `The customer confirmed "${job.title}" is complete`,
          { jobId: job.id, status: 'COMPLETED' }
        );
      }
      await emitJobStatusUpdate(job);
      return updated;
    }

    case 'cancel': {
      if (!isCustomer && !isAdmin) {
        throw new AppError('Only the customer can cancel this job', 403);
      }
      if (['COMPLETED', 'CANCELLED'].includes(job.status)) {
        throw new AppError('This job cannot be cancelled', 400);
      }
      const updated = await prisma.job.update({
        where: { id: job.id },
        data: { status: 'CANCELLED' },
        include: { category: true, worker: { include: { user: true } } },
      });
      if (job.worker?.userId) {
        await createAndEmitNotification(
          job.worker.userId,
          'JOB_STATUS_CHANGED',
          'Job cancelled',
          `"${job.title}" was cancelled by the customer`,
          { jobId: job.id, status: 'CANCELLED' }
        );
      }
      await emitJobStatusUpdate(job);
      return updated;
    }

    case 'reopen': {
      if (!isCustomer && !isAdmin) {
        throw new AppError('Only the customer can reopen this job', 403);
      }
      if (!['ACCEPTED', 'IN_PROGRESS', 'CANCELLED'].includes(job.status)) {
        throw new AppError('Only hired or cancelled jobs can be reopened for new quotes', 400);
      }

      const formerWorkerId = job.workerId;
      const formerWorkerUserId = job.worker?.userId ?? null;

      const updated = await prisma.$transaction(async (tx) => {
        if (formerWorkerId) {
          await tx.quotation.updateMany({
            where: { jobId: job.id, workerId: formerWorkerId, status: 'ACCEPTED' },
            data: { status: 'REJECTED' },
          });
        }

        await tx.quotation.updateMany({
          where: {
            jobId: job.id,
            status: 'REJECTED',
            ...(formerWorkerId ? { workerId: { not: formerWorkerId } } : {}),
          },
          data: { status: 'PENDING' },
        });

        const pendingCount = await tx.quotation.count({
          where: { jobId: job.id, status: 'PENDING' },
        });

        return tx.job.update({
          where: { id: job.id },
          data: {
            status: pendingCount > 0 ? JobStatus.QUOTED : JobStatus.OPEN,
            workerId: null,
            workerMarkedCompleteAt: null,
            completedAt: null,
          },
          include: { category: true, worker: { include: { user: true } } },
        });
      });

      if (formerWorkerUserId) {
        await createAndEmitNotification(
          formerWorkerUserId,
          'JOB_STATUS_CHANGED',
          'Job reopened',
          `The customer reopened "${job.title}" and is looking for another worker`,
          { jobId: job.id, status: updated.status }
        );
      }

      try {
        const categoryWorkers = await prisma.workerSkill.findMany({
          where: { categoryId: updated.categoryId },
          include: {
            worker: {
              include: { user: { select: { id: true } } },
            },
          },
        });

        const notified = new Set<string>();
        if (formerWorkerUserId) notified.add(formerWorkerUserId);
        if (job.customerId) notified.add(job.customerId);

        for (const ws of categoryWorkers) {
          const workerUserId = ws.worker.user.id;
          if (notified.has(workerUserId)) continue;
          notified.add(workerUserId);

          await createAndEmitNotification(
            workerUserId,
            'JOB_POSTED',
            'Job open again',
            `"${job.title}" is accepting quotes again`,
            { jobId: job.id, categoryId: updated.categoryId, reopened: true }
          );
        }

        const io = getIO();
        io.to(`category:${updated.categoryId}`).emit('new-job', updated);
      } catch (e) {
        console.error('Reopen notification error:', e);
      }

      await emitJobStatusUpdate({ id: job.id, customerId: job.customerId, worker: null });
      return updated;
    }

    default:
      throw new AppError('Invalid action', 400);
  }
}
