import { Router } from 'express';
import prisma from '../lib/prisma';
import { asyncHandler, sendSuccess, getPagination, buildPaginationMeta, AppError, paramId } from '../lib/utils';
import { workerServiceAreaFilter } from '../lib/serviceArea';
import { getWorkerReviewStats, applyReviewStats } from '../lib/reviewStats';
import { optionalAuth, authenticate, authorize, AuthRequest } from '../middleware/auth';
import { syncWorkerRankScore } from '../lib/workerRank';
import {
  notifyAvailabilityChange,
  notifyManualVerification,
} from '../services/adminNotify.service';
import { workerSearchOrClauses } from '../lib/searchQuery';
import { withPublicWorkerUser } from '../lib/workerDisplayName';

const router = Router();

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

    const where: Record<string, unknown> = { deletedAt: null };
    if (verified === 'true') where.isVerified = true;
    if (verified === 'false') where.isVerified = false;
    if (available === 'true') where.isAvailable = true;
    if (available === 'false') where.isAvailable = false;

    const userFilter: Record<string, unknown> = {};
    if (phoneVerified === 'true') userFilter.phoneVerified = true;
    if (phoneVerified === 'false') userFilter.phoneVerified = false;
    if (status === 'ACTIVE' || status === 'SUSPENDED') userFilter.status = String(status);
    if (Object.keys(userFilter).length > 0) where.user = userFilter;

    if (search) {
      const searchOr = workerSearchOrClauses(String(search));
      if (searchOr.length > 0) where.OR = searchOr;
    }

    const [workers, total, verifiedCount, unverifiedCount, phonePendingCount] = await Promise.all([
      prisma.workerProfile.findMany({
        where,
        orderBy: [{ isVerified: 'asc' }, { rankScore: 'desc' }, { createdAt: 'desc' }],
        skip,
        take: limit,
        include: {
          user: {
            select: {
              id: true,
              firstName: true,
              lastName: true,
              email: true,
              phone: true,
              avatarUrl: true,
              status: true,
              phoneVerified: true,
              emailVerified: true,
              createdAt: true,
              lastLoginAt: true,
            },
          },
          skills: { include: { category: { select: { name: true, slug: true, id: true } } } },
          _count: { select: { reviewsReceived: true, verificationDocs: true } },
        },
      }),
      prisma.workerProfile.count({ where }),
      prisma.workerProfile.count({ where: { deletedAt: null, isVerified: true } }),
      prisma.workerProfile.count({ where: { deletedAt: null, isVerified: false } }),
      prisma.workerProfile.count({
        where: { deletedAt: null, user: { phoneVerified: false } },
      }),
    ]);

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

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

    if (typeof isVerified === 'boolean') {
      data.isVerified = isVerified;
      data.verificationStatus = isVerified ? 'APPROVED' : 'PENDING';
    }
    if (typeof isAvailable === 'boolean') data.isAvailable = isAvailable;
    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 before = await prisma.workerProfile.findUnique({
      where: { id: paramId(req.params.id) },
      select: { isVerified: true, isAvailable: true, userId: true },
    });
    if (!before) throw new AppError('Worker not found', 404);

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

    await syncWorkerRankScore(worker.id);

    if (typeof isVerified === 'boolean' && isVerified !== before.isVerified) {
      await notifyManualVerification({
        userId: worker.user.id,
        role: 'WORKER',
        isVerified,
      });
    }
    if (typeof isAvailable === 'boolean' && isAvailable !== before.isAvailable) {
      await notifyAvailabilityChange({
        userId: worker.user.id,
        isAvailable,
      });
    }

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

router.get(
  '/',
  optionalAuth,
  asyncHandler(async (req, res) => {
    const { page, limit, skip } = getPagination(req.query);
    const {
      search,
      categoryId,
      categorySlug,
      location,
      area,
      gender,
      sort,
      minRating,
    } = req.query;

    const where: Record<string, unknown> = {
      deletedAt: null,
      isAvailable: true,
      isVerified: true,
    };

    if (minRating) where.averageRating = { gte: parseFloat(String(minRating)) };

    const genderFilter =
      gender && ['MALE', 'FEMALE', 'OTHER', 'PREFER_NOT_TO_SAY'].includes(String(gender))
        ? String(gender)
        : null;
    if (genderFilter) {
      where.user = { gender: genderFilter };
    }

    if (categoryId || categorySlug) {
      where.skills = {
        some: {
          category: {
            ...(categoryId ? { id: String(categoryId) } : {}),
            ...(categorySlug ? { slug: String(categorySlug) } : {}),
          },
        },
      };
    }

    const serviceAreaFilter = workerServiceAreaFilter(area ?? location);
    if (serviceAreaFilter) {
      where.serviceAreas = serviceAreaFilter;
    }

    if (search) {
      const searchOr = workerSearchOrClauses(String(search));
      if (searchOr.length > 0) where.OR = searchOr;
    }

    let orderBy: Record<string, string> | Record<string, string>[] = [
      { rankScore: 'desc' },
      { createdAt: 'desc' },
    ];
    const sortParam = sort ? String(sort) : 'recommended';
    if (sortParam === 'rating') {
      orderBy = [{ averageRating: 'desc' }, { totalReviews: 'desc' }];
    } else if (sortParam === 'reviews') {
      orderBy = { totalReviews: 'desc' };
    } else if (sortParam === 'experience') {
      orderBy = { experienceYears: 'desc' };
    } else if (sortParam === 'newest') {
      orderBy = { createdAt: 'desc' };
    } else if (sortParam === 'recommended') {
      orderBy = [{ rankScore: 'desc' }, { createdAt: 'desc' }];
    }

    const [workers, total] = await Promise.all([
      prisma.workerProfile.findMany({
        where,
        orderBy,
        skip,
        take: limit,
        include: {
          user: {
            select: {
              id: true,
              firstName: true,
              lastName: true,
              avatarUrl: true,
              lastActiveAt: true,
            },
          },
          skills: { include: { category: true } },
          serviceAreas: true,
          portfolioImages: { take: 3, orderBy: { sortOrder: 'asc' } },
          _count: { select: { reviewsReceived: true, portfolioImages: true } },
        },
      }),
      prisma.workerProfile.count({ where }),
    ]);

    const enriched = workers.map((worker) => {
      const publicWorker = withPublicWorkerUser(worker);
      return {
        ...publicWorker,
        totalReviews: worker._count.reviewsReceived,
      };
    });

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

router.get(
  '/:id',
  optionalAuth,
  asyncHandler(async (req: AuthRequest, res) => {
    const worker = await prisma.workerProfile.findFirst({
      where: { id: paramId(req.params.id), deletedAt: null },
      include: {
        user: {
          select: {
            id: true,
            firstName: true,
            lastName: true,
            avatarUrl: true,
            phone: true,
            lastActiveAt: true,
          },
        },
        skills: { include: { category: true } },
        serviceAreas: true,
        availability: { where: { isActive: true } },
        portfolioImages: { orderBy: { sortOrder: 'asc' } },
        reviewsReceived: {
          take: 10,
          orderBy: { createdAt: 'desc' },
          include: {
            customer: {
              select: { firstName: true, lastName: true, avatarUrl: true },
            },
            job: { select: { title: true } },
          },
        },
      },
    });

    if (!worker) throw new AppError('Worker not found', 404);

    const isOwner = req.user?.userId === worker.userId;
    const isAdmin = req.user?.role === 'ADMIN';
    if (!worker.isVerified && !isOwner && !isAdmin) {
      throw new AppError('This worker profile is not publicly visible yet', 404);
    }

    const stats = await getWorkerReviewStats(worker.id);
    const payload = applyReviewStats(worker, stats);
    // Public profile always shows display name (even to owner previewing).
    sendSuccess(res, withPublicWorkerUser(payload));
  })
);

export default router;
