import { Router } from 'express';
import multer from 'multer';
import prisma from '../lib/prisma';
import { storage } from '../lib/storage';
import {
  asyncHandler,
  sendSuccess,
  getPagination,
  buildPaginationMeta,
  AppError,
  validate,
  paramId,
} from '../lib/utils';
import { authenticate, authorize, AuthRequest } from '../middleware/auth';
import { updateWorkerProfileSchema, saveWorkerSchema, updateProfileSchema } from '../validators/schemas';
import { syncWorkerRankScore } from '../lib/workerRank';
import { withPublicWorkerUser } from '../lib/workerDisplayName';
import { MAX_WORKER_PORTFOLIO_IMAGES } from '@ehandyhire/shared';
import {
  notifyAccountStatusChange,
  notifyManualVerification,
} from '../services/adminNotify.service';
import { getWorkerReviewStats } from '../lib/reviewStats';
import { conversationListWhere, getWorkerProfileId } from '../services/messages.service';
import {
  ACCOUNT_DELETION_GRACE_HOURS,
  cancelAccountDeletion,
  processAccountDeletion,
  requestAccountDeletion,
} from '../services/accountDeletion.service';

const router = Router();

const imageUpload = multer({
  storage: multer.memoryStorage(),
  limits: { fileSize: 10 * 1024 * 1024 },
  fileFilter: (_req, file, cb) => {
    if (file.mimetype.startsWith('image/')) {
      cb(null, true);
    } else {
      cb(new Error('Only image files are allowed'));
    }
  },
});

router.get(
  '/',
  authenticate,
  authorize('ADMIN'),
  asyncHandler(async (req, res) => {
    const { page, limit, skip } = getPagination(req.query);
    const { role, status, search } = req.query;

    const where: Record<string, unknown> = { deletedAt: null };
    if (role) where.role = String(role);
    if (status) where.status = String(status);
    if (search) {
      where.OR = [
        { firstName: { contains: String(search) } },
        { lastName: { contains: String(search) } },
        { email: { contains: String(search) } },
        { phone: { contains: String(search) } },
      ];
    }

    const [users, total] = await Promise.all([
      prisma.user.findMany({
        where,
        skip,
        take: limit,
        orderBy: { createdAt: 'desc' },
        select: {
          id: true,
          email: true,
          phone: true,
          firstName: true,
          lastName: true,
          role: true,
          status: true,
          phoneVerified: true,
          emailVerified: true,
          createdAt: true,
          lastLoginAt: true,
        },
      }),
      prisma.user.count({ where }),
    ]);

    sendSuccess(res, users, undefined, 200, buildPaginationMeta(total, page, limit));
  })
);

router.get(
  '/admin/customers',
  authenticate,
  authorize('ADMIN'),
  asyncHandler(async (req, res) => {
    const { page, limit, skip } = getPagination(req.query);
    const { search, verified, phoneVerified, status } = req.query;

    const where: Record<string, unknown> = {
      deletedAt: null,
      role: 'CUSTOMER',
    };
    if (search) {
      where.OR = [
        { firstName: { contains: String(search) } },
        { lastName: { contains: String(search) } },
        { email: { contains: String(search) } },
        { phone: { contains: String(search) } },
      ];
    }
    if (verified === 'true') {
      where.customerProfile = { isVerified: true };
    } else if (verified === 'false') {
      where.customerProfile = { isVerified: false };
    }
    if (phoneVerified === 'true') where.phoneVerified = true;
    if (phoneVerified === 'false') where.phoneVerified = false;
    if (status === 'ACTIVE' || status === 'SUSPENDED') where.status = String(status);

    const [customers, total, verifiedCount, unverifiedCount, phonePendingCount] = await Promise.all([
      prisma.user.findMany({
        where,
        skip,
        take: limit,
        orderBy: { createdAt: 'desc' },
        select: {
          id: true,
          email: true,
          phone: true,
          firstName: true,
          lastName: true,
          status: true,
          phoneVerified: true,
          emailVerified: true,
          createdAt: true,
          lastLoginAt: true,
          customerProfile: {
            select: {
              id: true,
              isVerified: true,
              verificationStatus: true,
              _count: { select: { verificationDocs: true } },
            },
          },
        },
      }),
      prisma.user.count({ where }),
      prisma.user.count({
        where: { deletedAt: null, role: 'CUSTOMER', customerProfile: { isVerified: true } },
      }),
      prisma.user.count({
        where: { deletedAt: null, role: 'CUSTOMER', customerProfile: { isVerified: false } },
      }),
      prisma.user.count({
        where: { deletedAt: null, role: 'CUSTOMER', phoneVerified: false },
      }),
    ]);

    sendSuccess(
      res,
      {
        items: customers,
        summary: {
          verified: verifiedCount,
          unverified: unverifiedCount,
          phonePending: phonePendingCount,
          total: verifiedCount + unverifiedCount,
        },
      },
      undefined,
      200,
      buildPaginationMeta(total, page, limit)
    );
  })
);

router.patch(
  '/admin/customers/:id',
  authenticate,
  authorize('ADMIN'),
  asyncHandler(async (req, res) => {
    const { isVerified, verificationStatus } = req.body;
    const data: Record<string, unknown> = {};

    if (typeof isVerified === 'boolean') {
      data.isVerified = isVerified;
      data.verificationStatus = isVerified ? 'APPROVED' : 'PENDING';
    }
    if (
      verificationStatus &&
      ['PENDING', 'APPROVED', 'REJECTED', 'ADDITIONAL_DOCS_REQUIRED'].includes(verificationStatus)
    ) {
      data.verificationStatus = verificationStatus;
    }

    if (Object.keys(data).length === 0) {
      throw new AppError('No valid fields to update', 400);
    }

    const profileId = paramId(req.params.id);
    const before = await prisma.customerProfile.findUnique({
      where: { id: profileId },
      select: { id: true, isVerified: true, userId: true },
    });
    if (!before) throw new AppError('Customer profile not found', 404);

    const customer = await prisma.customerProfile.update({
      where: { id: profileId },
      data,
      include: {
        user: {
          select: {
            id: true,
            firstName: true,
            lastName: true,
            email: true,
            phone: true,
            status: true,
            phoneVerified: true,
            emailVerified: true,
          },
        },
      },
    });

    if (typeof isVerified === 'boolean' && isVerified !== before.isVerified) {
      await notifyManualVerification({
        userId: customer.user.id,
        role: 'CUSTOMER',
        isVerified,
      });
    }

    sendSuccess(res, customer, 'Customer updated');
  })
);

router.post(
  '/me/deletion-request',
  authenticate,
  authorize('CUSTOMER', 'WORKER'),
  asyncHandler(async (req: AuthRequest, res) => {
    const reason =
      typeof req.body?.reason === 'string' ? req.body.reason.trim().slice(0, 1000) : undefined;
    try {
      const result = await requestAccountDeletion(req.user!.userId, reason);
      sendSuccess(
        res,
        {
          ...result,
          graceHours: ACCOUNT_DELETION_GRACE_HOURS,
        },
        `Account removal scheduled in ${ACCOUNT_DELETION_GRACE_HOURS} hours`
      );
    } catch (err) {
      const code = err instanceof Error ? err.message : '';
      if (code === 'ALREADY_REQUESTED') {
        throw new AppError('You already have a pending account removal request', 409);
      }
      if (code === 'ADMIN_FORBIDDEN') {
        throw new AppError('Admin accounts cannot be removed this way', 403);
      }
      throw new AppError('Unable to schedule account removal', 400);
    }
  })
);

router.delete(
  '/me/deletion-request',
  authenticate,
  authorize('CUSTOMER', 'WORKER'),
  asyncHandler(async (req: AuthRequest, res) => {
    try {
      const result = await cancelAccountDeletion(req.user!.userId, false);
      sendSuccess(res, result, 'Account removal cancelled');
    } catch (err) {
      const code = err instanceof Error ? err.message : '';
      if (code === 'NO_PENDING_REQUEST') {
        throw new AppError('No pending account removal request', 404);
      }
      throw new AppError('Unable to cancel account removal', 400);
    }
  })
);

router.get(
  '/admin/deletion-requests',
  authenticate,
  authorize('ADMIN'),
  asyncHandler(async (req, res) => {
    const { page, limit, skip } = getPagination(req.query);
    const where = {
      deletedAt: null,
      status: { not: 'DELETED' as const },
      deletionScheduledFor: { not: null },
    };

    const [items, total] = await Promise.all([
      prisma.user.findMany({
        where,
        skip,
        take: limit,
        orderBy: { deletionScheduledFor: 'asc' },
        select: {
          id: true,
          email: true,
          phone: true,
          firstName: true,
          lastName: true,
          role: true,
          status: true,
          deletionRequestedAt: true,
          deletionScheduledFor: true,
          deletionReason: true,
          createdAt: true,
        },
      }),
      prisma.user.count({ where }),
    ]);

    sendSuccess(
      res,
      {
        items,
        graceHours: ACCOUNT_DELETION_GRACE_HOURS,
      },
      undefined,
      200,
      buildPaginationMeta(total, page, limit)
    );
  })
);

router.patch(
  '/admin/deletion-requests/:id/cancel',
  authenticate,
  authorize('ADMIN'),
  asyncHandler(async (req, res) => {
    try {
      const result = await cancelAccountDeletion(paramId(req.params.id), true);
      sendSuccess(res, result, 'Deletion request cancelled');
    } catch (err) {
      const code = err instanceof Error ? err.message : '';
      if (code === 'NO_PENDING_REQUEST') {
        throw new AppError('No pending deletion request for this user', 404);
      }
      throw new AppError('Unable to cancel deletion request', 400);
    }
  })
);

router.patch(
  '/admin/deletion-requests/:id/process',
  authenticate,
  authorize('ADMIN'),
  asyncHandler(async (req, res) => {
    const userId = paramId(req.params.id);
    const pending = await prisma.user.findFirst({
      where: {
        id: userId,
        deletedAt: null,
        deletionScheduledFor: { not: null },
      },
      select: { id: true },
    });
    if (!pending) {
      throw new AppError('No pending deletion request for this user', 404);
    }

    await processAccountDeletion(userId);
    sendSuccess(res, { id: userId }, 'Account removed');
  })
);

router.patch(
  '/:id/status',
  authenticate,
  authorize('ADMIN'),
  asyncHandler(async (req, res) => {
    const { status } = req.body;
    if (!['ACTIVE', 'SUSPENDED'].includes(status)) {
      throw new AppError('Invalid status', 400);
    }

    const before = await prisma.user.findUnique({
      where: { id: paramId(req.params.id) },
      select: { id: true, status: true, role: true },
    });
    if (!before) throw new AppError('User not found', 404);

    const user = await prisma.user.update({
      where: { id: paramId(req.params.id) },
      data: { status },
      select: { id: true, firstName: true, lastName: true, status: true, role: true, email: true },
    });

    if (before.status !== status) {
      await notifyAccountStatusChange({
        userId: user.id,
        status,
        role: user.role,
      });
    }

    sendSuccess(res, user, `User ${status.toLowerCase()}`);
  })
);

router.patch(
  '/profile',
  authenticate,
  validate(updateProfileSchema),
  asyncHandler(async (req: AuthRequest, res) => {
    const { firstName, lastName, gender, address, city, bio } = req.body;

    const user = await prisma.user.update({
      where: { id: req.user!.userId },
      data: {
        ...(firstName && { firstName }),
        ...(lastName && { lastName }),
        ...(gender !== undefined && { gender }),
      },
      select: {
        id: true,
        email: true,
        phone: true,
        firstName: true,
        lastName: true,
        gender: true,
        avatarUrl: true,
        role: true,
      },
    });

    if (req.user!.role === 'CUSTOMER') {
      await prisma.customerProfile.upsert({
        where: { userId: req.user!.userId },
        update: { address, city, bio },
        create: { userId: req.user!.userId, address, city, bio },
      });
    }

    sendSuccess(res, user, 'Profile updated');
  })
);

router.post(
  '/avatar',
  authenticate,
  imageUpload.single('avatar'),
  asyncHandler(async (req: AuthRequest, res) => {
    if (!req.file) throw new AppError('Image required', 400);

    const existing = await prisma.user.findUnique({
      where: { id: req.user!.userId },
      select: { avatarUrl: true },
    });
    if (!existing) throw new AppError('User not found', 404);

    if (existing.avatarUrl) {
      try {
        await storage.delete(existing.avatarUrl);
      } catch {
        // file may already be gone
      }
    }

    const url = await storage.upload(req.file, 'avatars');
    const user = await prisma.user.update({
      where: { id: req.user!.userId },
      data: { avatarUrl: url },
      select: {
        id: true,
        firstName: true,
        lastName: true,
        avatarUrl: true,
      },
    });

    sendSuccess(res, user, 'Profile photo updated');
  })
);

router.delete(
  '/avatar',
  authenticate,
  asyncHandler(async (req: AuthRequest, res) => {
    const existing = await prisma.user.findUnique({
      where: { id: req.user!.userId },
      select: { avatarUrl: true },
    });
    if (!existing) throw new AppError('User not found', 404);

    if (existing.avatarUrl) {
      try {
        await storage.delete(existing.avatarUrl);
      } catch {
        // file may already be gone
      }
    }

    const user = await prisma.user.update({
      where: { id: req.user!.userId },
      data: { avatarUrl: null },
      select: {
        id: true,
        firstName: true,
        lastName: true,
        avatarUrl: true,
      },
    });

    sendSuccess(res, user, 'Profile photo removed');
  })
);

router.patch(
  '/worker/profile',
  authenticate,
  authorize('WORKER'),
  validate(updateWorkerProfileSchema),
  asyncHandler(async (req: AuthRequest, res) => {
    const { displayName, headline, bio, experienceYears, hourlyRate, rateType, isAvailable, otherSkills, isStudent, categoryIds, serviceAreas } =
      req.body;

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

    if (isAvailable === true && worker.isHired) {
      throw new AppError(
        'You are currently hired. Mark the hire as complete before setting yourself available again.',
        400
      );
    }

    await prisma.workerProfile.update({
      where: { id: worker.id },
      data: {
        ...(displayName !== undefined && { displayName: displayName.trim() }),
        ...(headline !== undefined && { headline }),
        ...(bio !== undefined && { bio }),
        ...(experienceYears !== undefined && { experienceYears }),
        ...(hourlyRate !== undefined && { hourlyRate }),
        ...(rateType !== undefined && { rateType }),
        ...(isAvailable !== undefined && { isAvailable }),
        ...(otherSkills !== undefined && { otherSkills: otherSkills?.trim() || null }),
        ...(isStudent !== undefined && { isStudent }),
      },
    });

    if (categoryIds) {
      let ids = [...categoryIds];
      if (otherSkills?.trim()) {
        const otherCategory = await prisma.category.findFirst({
          where: { slug: 'other', deletedAt: null },
        });
        if (otherCategory && !ids.includes(otherCategory.id)) {
          ids.push(otherCategory.id);
        }
      }

      await prisma.workerSkill.deleteMany({ where: { workerId: worker.id } });
      if (ids.length > 0) {
        await prisma.workerSkill.createMany({
          data: ids.map((categoryId: string) => ({
            workerId: worker.id,
            categoryId,
          })),
        });
      }
    }

    if (serviceAreas) {
      await prisma.workerServiceArea.deleteMany({ where: { workerId: worker.id } });
      await prisma.workerServiceArea.createMany({
        data: serviceAreas.map((area: string) => ({
          workerId: worker.id,
          area,
        })),
      });
    }

    const result = await prisma.workerProfile.findUnique({
      where: { id: worker.id },
      include: {
        skills: { include: { category: true } },
        serviceAreas: true,
      },
    });

    await syncWorkerRankScore(worker.id);

    sendSuccess(res, result, 'Worker profile updated');
  })
);

router.get(
  '/worker/portfolio',
  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 images = await prisma.portfolioImage.findMany({
      where: { workerId: worker.id },
      orderBy: { sortOrder: 'asc' },
    });

    sendSuccess(res, images);
  })
);

router.post(
  '/worker/portfolio',
  authenticate,
  authorize('WORKER'),
  imageUpload.single('image'),
  asyncHandler(async (req: AuthRequest, res) => {
    const { title, description } = req.body;
    if (!req.file) throw new AppError('Image required', 400);

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

    const count = await prisma.portfolioImage.count({ where: { workerId: worker.id } });
    if (count >= MAX_WORKER_PORTFOLIO_IMAGES) {
      throw new AppError(
        `Maximum ${MAX_WORKER_PORTFOLIO_IMAGES} portfolio photos allowed`,
        400
      );
    }

    const url = await storage.upload(req.file, 'portfolio');
    const image = await prisma.portfolioImage.create({
      data: {
        workerId: worker.id,
        url,
        title: title || null,
        description: description || null,
        sortOrder: count,
      },
    });

    await syncWorkerRankScore(worker.id);

    sendSuccess(res, image, 'Portfolio image added', 201);
  })
);

router.delete(
  '/worker/portfolio/:id',
  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 image = await prisma.portfolioImage.findFirst({
      where: { id: paramId(req.params.id), workerId: worker.id },
    });
    if (!image) throw new AppError('Image not found', 404);

    await prisma.portfolioImage.delete({ where: { id: image.id } });
    try {
      await storage.delete(image.url);
    } catch {
      // file may already be gone
    }

    await syncWorkerRankScore(worker.id);

    sendSuccess(res, null, 'Portfolio image removed');
  })
);

router.get(
  '/worker/dashboard',
  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 userId = req.user!.userId;
    const workerProfileId = await getWorkerProfileId(userId);

    const [
      availableJobs,
      myQuotations,
      acceptedJobs,
      unreadNotifications,
      unreadMessages,
      pendingHireInvitations,
      reviewStats,
    ] = await Promise.all([
      prisma.job.count({
        where: {
          status: { in: ['OPEN', 'QUOTED'] },
          deletedAt: null,
          categoryId: {
            in: (
              await prisma.workerSkill.findMany({
                where: { workerId: worker.id },
                select: { categoryId: true },
              })
            ).map((s) => s.categoryId),
          },
        },
      }),
      prisma.quotation.count({ where: { workerId: worker.id } }),
      prisma.job.count({
        where: { workerId: worker.id, status: { in: ['ACCEPTED', 'IN_PROGRESS'] } },
      }),
      prisma.notification.count({ where: { userId, isRead: false } }),
      prisma.message.count({
        where: {
          isRead: false,
          senderId: { not: userId },
          conversation: conversationListWhere(userId, workerProfileId ?? undefined),
        },
      }),
      prisma.hireInvitation.count({
        where: { workerId: worker.id, status: 'PENDING' },
      }),
      getWorkerReviewStats(worker.id),
    ]);

    const averageRating = Number(reviewStats._avg.overallRating ?? worker.averageRating ?? 0);
    const totalReviews = reviewStats._count;

    // Heal drifted denormalized counters when reviews exist
    if (
      totalReviews !== worker.totalReviews ||
      Math.abs(averageRating - Number(worker.averageRating || 0)) > 0.01
    ) {
      void prisma.workerProfile
        .update({
          where: { id: worker.id },
          data: { totalReviews, averageRating },
        })
        .catch(() => undefined);
    }

    sendSuccess(res, {
      worker: {
        id: worker.id,
        isVerified: worker.isVerified,
        isAvailable: worker.isAvailable,
        isHired: worker.isHired,
        totalJobs: worker.totalJobs,
        averageRating,
        totalReviews,
      },
      stats: {
        availableJobs,
        myQuotations,
        acceptedJobs,
        unreadNotifications,
        unreadMessages,
        pendingHireInvitations,
        totalJobs: worker.totalJobs,
        averageRating,
        totalReviews,
      },
    });
  })
);

router.get(
  '/customer/dashboard',
  authenticate,
  authorize('CUSTOMER'),
  asyncHandler(async (req: AuthRequest, res) => {
    const [openJobs, activeJobs, completedJobs, unreadNotifications] = await Promise.all([
      prisma.job.count({ where: { customerId: req.user!.userId, status: 'OPEN', deletedAt: null } }),
      prisma.job.count({
        where: {
          customerId: req.user!.userId,
          status: { in: ['ACCEPTED', 'IN_PROGRESS', 'QUOTED'] },
          deletedAt: null,
        },
      }),
      prisma.job.count({ where: { customerId: req.user!.userId, status: 'COMPLETED', deletedAt: null } }),
      prisma.notification.count({ where: { userId: req.user!.userId, isRead: false } }),
    ]);

    sendSuccess(res, {
      stats: { openJobs, activeJobs, completedJobs, unreadNotifications },
    });
  })
);

router.get(
  '/customer/saved-workers',
  authenticate,
  authorize('CUSTOMER'),
  asyncHandler(async (req: AuthRequest, res) => {
    const saved = await prisma.savedWorker.findMany({
      where: { customerId: req.user!.userId },
      orderBy: { createdAt: 'desc' },
      include: {
        worker: {
          include: {
            user: {
              select: { firstName: true, lastName: true, avatarUrl: true },
            },
            skills: { include: { category: true } },
            serviceAreas: true,
          },
        },
      },
    });

    const workerIds = saved.map((entry) => entry.worker.id);
    const priorHires =
      workerIds.length > 0
        ? await prisma.job.findMany({
            where: {
              customerId: req.user!.userId,
              workerId: { in: workerIds },
              status: { in: ['ACCEPTED', 'IN_PROGRESS', 'COMPLETED'] },
              deletedAt: null,
            },
            select: { workerId: true },
            distinct: ['workerId'],
          })
        : [];
    const hiredWorkerIds = new Set(
      priorHires.map((job) => job.workerId).filter(Boolean) as string[]
    );

    sendSuccess(
      res,
      saved.map((entry) => ({
        savedAt: entry.createdAt,
        hasHiredBefore: hiredWorkerIds.has(entry.worker.id),
        ...withPublicWorkerUser(entry.worker),
      }))
    );
  })
);

router.get(
  '/customer/saved-workers/check/:workerId',
  authenticate,
  authorize('CUSTOMER'),
  asyncHandler(async (req: AuthRequest, res) => {
    const workerId = paramId(req.params.workerId);
    const existing = await prisma.savedWorker.findUnique({
      where: {
        customerId_workerId: {
          customerId: req.user!.userId,
          workerId,
        },
      },
    });
    sendSuccess(res, { saved: Boolean(existing) });
  })
);

router.post(
  '/customer/saved-workers',
  authenticate,
  authorize('CUSTOMER'),
  validate(saveWorkerSchema),
  asyncHandler(async (req: AuthRequest, res) => {
    const { workerId } = req.body;

    const worker = await prisma.workerProfile.findFirst({
      where: { id: workerId, deletedAt: null },
    });
    if (!worker) throw new AppError('Worker not found', 404);

    const existing = await prisma.savedWorker.findUnique({
      where: {
        customerId_workerId: {
          customerId: req.user!.userId,
          workerId,
        },
      },
    });
    if (existing) throw new AppError('Worker already saved', 409);

    const saved = await prisma.savedWorker.create({
      data: {
        customerId: req.user!.userId,
        workerId,
      },
    });

    sendSuccess(res, saved, 'Worker saved', 201);
  })
);

router.delete(
  '/customer/saved-workers/:workerId',
  authenticate,
  authorize('CUSTOMER'),
  asyncHandler(async (req: AuthRequest, res) => {
    const workerId = paramId(req.params.workerId);

    const existing = await prisma.savedWorker.findUnique({
      where: {
        customerId_workerId: {
          customerId: req.user!.userId,
          workerId,
        },
      },
    });
    if (!existing) throw new AppError('Worker not in saved list', 404);

    await prisma.savedWorker.delete({ where: { id: existing.id } });
    sendSuccess(res, null, 'Worker removed from saved list');
  })
);

export default router;
