import { Router } from 'express';
import prisma from '../lib/prisma';
import {
  asyncHandler,
  sendSuccess,
  AppError,
  validate,
  paramId,
} from '../lib/utils';
import { authenticate, authorize, AuthRequest } from '../middleware/auth';
import { createQuotationSchema } from '../validators/schemas';
import { getIO } from '../socket';
import { ensureJobConversation } from '../services/messages.service';

const router = Router();

router.post(
  '/',
  authenticate,
  authorize('WORKER'),
  validate(createQuotationSchema),
  asyncHandler(async (req: AuthRequest, res) => {
    const { jobId, amount, estimatedDays, estimatedHours, message } = req.body;

    const worker = await prisma.workerProfile.findUnique({
      where: { userId: req.user!.userId },
    });
    if (!worker) throw new AppError('Worker profile not found', 404);

    const job = await prisma.job.findFirst({
      where: { id: jobId, status: { in: ['OPEN', 'QUOTED'] }, deletedAt: null },
      include: { category: true, customer: true },
    });
    if (!job) throw new AppError('Job not found or not accepting applications', 404);

    const hasSkill = await prisma.workerSkill.findFirst({
      where: { workerId: worker.id, categoryId: job.categoryId },
    });
    if (!hasSkill) {
      throw new AppError('You do not offer services in this job category', 400);
    }

    const existing = await prisma.quotation.findFirst({
      where: { jobId, workerId: worker.id },
    });
    if (existing?.status === 'PENDING') {
      throw new AppError('You already applied to this job', 409);
    }
    if (existing?.status === 'ACCEPTED') {
      throw new AppError('You are already hired on this job', 409);
    }

    const quotation = existing
      ? await prisma.quotation.update({
          where: { id: existing.id },
          data: {
            amount,
            estimatedDays,
            estimatedHours,
            message: message?.trim() || null,
            status: 'PENDING',
          },
          include: {
            worker: {
              include: {
                user: { select: { firstName: true, lastName: true, avatarUrl: true } },
              },
            },
          },
        })
      : await prisma.quotation.create({
          data: {
            jobId,
            workerId: worker.id,
            userId: req.user!.userId,
            amount,
            estimatedDays,
            estimatedHours,
            message,
          },
          include: {
            worker: {
              include: {
                user: { select: { firstName: true, lastName: true, avatarUrl: true } },
              },
            },
          },
        });

    if (job.status === 'OPEN') {
      await prisma.job.update({
        where: { id: jobId },
        data: { status: 'QUOTED' },
      });
    }

    if (job.customerId) {
      const notification = await prisma.notification.create({
        data: {
          userId: job.customerId,
          type: 'QUOTE_RECEIVED',
          title: 'New worker application',
          message: `A worker applied to "${job.title}" with an offer of PHP ${amount}`,
          data: { jobId, quotationId: quotation.id },
        },
      });

      try {
        const io = getIO();
        io.to(`user:${job.customerId}`).emit('notification', notification);
        io.to(`user:${job.customerId}`).emit('new-quote', quotation);
      } catch (e) {
        console.error('Socket error:', e);
      }
    }

    await ensureJobConversation(jobId);

    sendSuccess(res, quotation, 'Application submitted', 201);
  })
);

router.patch(
  '/:id/accept',
  authenticate,
  authorize('CUSTOMER'),
  asyncHandler(async (req: AuthRequest, res) => {
    const quotation = await prisma.quotation.findUnique({
      where: { id: paramId(req.params.id) },
      include: { job: true, worker: true },
    });

    if (!quotation) throw new AppError('Application not found', 404);
    if (quotation.job.customerId !== req.user!.userId) {
      throw new AppError('Not authorized', 403);
    }
    if (quotation.status !== 'PENDING') {
      throw new AppError('Application is no longer pending', 400);
    }

    const [updated] = await prisma.$transaction([
      prisma.quotation.update({
        where: { id: quotation.id },
        data: { status: 'ACCEPTED' },
        include: {
          worker: {
            include: { user: { select: { firstName: true, lastName: true } } },
          },
        },
      }),
      prisma.job.update({
        where: { id: quotation.jobId },
        data: {
          status: 'ACCEPTED',
          workerId: quotation.workerId,
        },
      }),
      prisma.quotation.updateMany({
        where: { jobId: quotation.jobId, id: { not: quotation.id }, status: 'PENDING' },
        data: { status: 'REJECTED' },
      }),
    ]);

    const workerUserId = quotation.worker.userId;
    const notification = await prisma.notification.create({
      data: {
        userId: workerUserId,
        type: 'QUOTE_ACCEPTED',
        title: 'Application accepted!',
        message: `Your application for "${quotation.job.title}" was accepted`,
        data: { jobId: quotation.jobId, quotationId: quotation.id },
      },
    });

    await ensureJobConversation(quotation.jobId);

    try {
      const io = getIO();
      io.to(`user:${workerUserId}`).emit('notification', notification);
      io.to(`user:${workerUserId}`).emit('job-status-update', {
        jobId: quotation.jobId,
        status: 'ACCEPTED',
      });
    } catch (e) {
      console.error('Socket error:', e);
    }

    sendSuccess(res, updated, 'Worker hired');
  })
);

router.patch(
  '/:id/reject',
  authenticate,
  authorize('CUSTOMER'),
  asyncHandler(async (req: AuthRequest, res) => {
    const quotation = await prisma.quotation.findUnique({
      where: { id: paramId(req.params.id) },
      include: { job: true, worker: true },
    });

    if (!quotation) throw new AppError('Application not found', 404);
    if (quotation.job.customerId !== req.user!.userId) {
      throw new AppError('Not authorized', 403);
    }

    const updated = await prisma.quotation.update({
      where: { id: quotation.id },
      data: { status: 'REJECTED' },
    });

    const notification = await prisma.notification.create({
      data: {
        userId: quotation.worker.userId,
        type: 'QUOTE_REJECTED',
        title: 'Application declined',
        message: `Your application for "${quotation.job.title}" was declined`,
        data: { jobId: quotation.jobId },
      },
    });

    try {
      const io = getIO();
      io.to(`user:${quotation.worker.userId}`).emit('notification', notification);
    } catch (e) {
      console.error('Socket error:', e);
    }

    sendSuccess(res, updated, 'Application declined');
  })
);

router.patch(
  '/:id/withdraw',
  authenticate,
  authorize('WORKER'),
  asyncHandler(async (req: AuthRequest, res) => {
    const worker = await prisma.workerProfile.findUnique({
      where: { userId: req.user!.userId },
      select: { id: true },
    });
    if (!worker) throw new AppError('Worker profile not found', 404);

    const quotation = await prisma.quotation.findUnique({
      where: { id: paramId(req.params.id) },
      include: { job: true },
    });

    if (!quotation) throw new AppError('Application not found', 404);
    if (quotation.workerId !== worker.id) throw new AppError('Not authorized', 403);
    if (quotation.status !== 'PENDING') {
      throw new AppError('Only pending applications can be withdrawn', 400);
    }

    const updated = await prisma.quotation.update({
      where: { id: quotation.id },
      data: { status: 'WITHDRAWN' },
    });

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

    if (pendingCount === 0 && quotation.job.status === 'QUOTED') {
      await prisma.job.update({
        where: { id: quotation.jobId },
        data: { status: 'OPEN' },
      });
    }

    sendSuccess(res, updated, 'Application withdrawn');
  })
);

router.get(
  '/my',
  authenticate,
  authorize('WORKER'),
  asyncHandler(async (req: AuthRequest, res) => {
    const worker = await prisma.workerProfile.findUnique({
      where: { userId: req.user!.userId },
    });
    if (!worker) throw new AppError('Worker profile not found', 404);

    const quotations = await prisma.quotation.findMany({
      where: { workerId: worker.id },
      orderBy: { createdAt: 'desc' },
      include: {
        job: {
          select: {
            id: true,
            title: true,
            status: true,
            category: { select: { name: true } },
          },
        },
      },
    });

    sendSuccess(res, quotations);
  })
);

router.get(
  '/job/:jobId',
  authenticate,
  asyncHandler(async (req: AuthRequest, res) => {
    const quotations = await prisma.quotation.findMany({
      where: { jobId: paramId(req.params.jobId) },
      include: {
        worker: {
          include: {
            user: { select: { firstName: true, lastName: true, avatarUrl: true } },
            skills: { include: { category: true } },
          },
        },
      },
      orderBy: { createdAt: 'desc' },
    });

    sendSuccess(res, quotations);
  })
);

export default router;
