import { NextRequest, NextResponse } from 'next/server'
import { db } from '@/lib/db'
import { requireAuth } from '@/lib/api-auth'
import { generateReviewCode } from '@/lib/review-code'
import { notifyUsers } from '@/lib/notifications'
import { writeFile, mkdir } from 'fs/promises'
import path from 'path'
import { Prisma } from '@prisma/client'

export async function GET(request: NextRequest) {
  try {
    const authResult = await requireAuth(['SUPERVISOR', 'MANAGER', 'ADMIN'])(request)
    if ('error' in authResult) return authResult.error

    const { user } = authResult

    const { searchParams } = new URL(request.url)
    const status = searchParams.get('status')
    const sentiment = searchParams.get('sentiment')
    const rating = searchParams.get('rating')
    const search = searchParams.get('search')
    const serviceType = searchParams.get('serviceType')
    const dateFrom = searchParams.get('dateFrom')
    const dateTo = searchParams.get('dateTo')
    const page = parseInt(searchParams.get('page') || '1')
    const limit = parseInt(searchParams.get('limit') || '10')

    const where: Prisma.ReviewWhereInput = {
      deletedAt: null,
    }

    // Supervisors only see reviews assigned to them
    if (user.role === 'SUPERVISOR') {
      where.assignedToId = user.id
    }

    if (status) where.status = status as Prisma.EnumReviewStatusFilter
    if (sentiment) where.sentiment = sentiment as Prisma.EnumReviewSentimentFilter
    if (rating) where.rating = parseInt(rating)
    if (serviceType) where.serviceType = serviceType
    if (search) {
      where.OR = [
        { title: { contains: search } },
        { content: { contains: search } },
        { reviewCode: { contains: search } },
        { customer: { name: { contains: search } } },
        { customer: { email: { contains: search } } },
      ]
    }
    if (dateFrom || dateTo) {
      where.createdAt = {}
      if (dateFrom) where.createdAt.gte = new Date(dateFrom)
      if (dateTo) where.createdAt.lte = new Date(dateTo)
    }

    const [reviews, total] = await Promise.all([
      db.review.findMany({
        where,
        include: {
          customer: { select: { id: true, name: true, email: true, company: true } },
          assignedTo: { select: { id: true, name: true } },
          _count: { select: { photos: true, responses: true } },
        },
        orderBy: { createdAt: 'desc' },
        skip: (page - 1) * limit,
        take: limit,
      }),
      db.review.count({ where }),
    ])

    return NextResponse.json({
      reviews,
      pagination: {
        page,
        limit,
        total,
        totalPages: Math.ceil(total / limit),
      },
    })
  } catch (error) {
    console.error('Error al listar reseñas:', error)
    return NextResponse.json(
      { error: 'Error interno del servidor' },
      { status: 500 }
    )
  }
}

export async function POST(request: NextRequest) {
  try {
    const authResult = await requireAuth(['CUSTOMER'])(request)
    if ('error' in authResult) return authResult.error

    const { user } = authResult

    // Parse body: support both JSON and FormData (when photos are attached)
    const contentType = request.headers.get('content-type') || ''
    let rating: number
    let title: string
    let content: string
    let serviceDate: string
    let serviceType: string
    let subType: string | undefined
    let photos: File[] = []

    if (contentType.includes('multipart/form-data')) {
      const formData = await request.formData()
      rating = parseInt(formData.get('rating') as string)
      title = formData.get('title') as string
      content = formData.get('content') as string
      serviceDate = formData.get('serviceDate') as string
      serviceType = formData.get('serviceType') as string
      subType = (formData.get('subType') as string) || undefined
      // Collect all photo files
      const photoEntries = formData.getAll('photos')
      photos = photoEntries.filter((p): p is File => p instanceof File)
    } else {
      const body = await request.json()
      rating = body.rating
      title = body.title
      content = body.content
      serviceDate = body.serviceDate
      serviceType = body.serviceType
      subType = body.subType
    }

    if (!rating || !title || !content || !serviceDate || !serviceType) {
      return NextResponse.json(
        { error: 'Todos los campos son requeridos: calificación, título, contenido, fecha de servicio, tipo de servicio' },
        { status: 400 }
      )
    }

    if (rating < 1 || rating > 5) {
      return NextResponse.json(
        { error: 'La calificación debe ser entre 1 y 5' },
        { status: 400 }
      )
    }

    // Validar que el cliente no tenga ya una reseña para este servicio en esta fecha
    const serviceDateParsed = new Date(serviceDate)
    const dayStart = new Date(serviceDateParsed)
    dayStart.setHours(0, 0, 0, 0)
    const dayEnd = new Date(serviceDateParsed)
    dayEnd.setHours(23, 59, 59, 999)

    const existingReview = await db.review.findFirst({
      where: {
        customerId: user.id,
        serviceType,
        deletedAt: null,
        serviceDate: {
          gte: dayStart,
          lte: dayEnd,
        },
      },
    })

    if (existingReview) {
      return NextResponse.json(
        {
          error: 'Ya existe una reseña para este tipo de servicio en esa fecha.',
          existingReviewCode: existingReview.reviewCode,
        },
        { status: 409 }
      )
    }

    const sentiment = rating <= 2 ? 'NEGATIVE' as const : 'POSITIVE' as const
    const reviewStatus = sentiment === 'POSITIVE' ? 'RESOLVED' as const : 'PENDING' as const

    // Assign to the customer's assigned supervisor (from the assignedById field)
    const customer = await db.user.findUnique({
      where: { id: user.id },
      select: { assignedById: true, company: true },
    })

    let assignedToId: string | null = null
    if (sentiment === 'NEGATIVE' && customer?.assignedById) {
      // Assign to the supervisor who manages this customer
      assignedToId = customer.assignedById
    } else if (sentiment === 'NEGATIVE') {
      // Fallback: if no assigned supervisor, use round-robin
      const supervisors = await db.user.findMany({
        where: { role: 'SUPERVISOR', isActive: true },
        select: { id: true },
      })
      if (supervisors.length > 0) {
        const assignmentCounts = await Promise.all(
          supervisors.map(async (sup) => ({
            id: sup.id,
            count: await db.review.count({
              where: { assignedToId: sup.id, status: { in: ['PENDING', 'ACKNOWLEDGED'] }, deletedAt: null },
            }),
          }))
        )
        assignmentCounts.sort((a, b) => a.count - b.count)
        assignedToId = assignmentCounts[0].id
      }
    }

    const reviewCode = await generateReviewCode()

    const review = await db.review.create({
      data: {
        reviewCode,
        customerId: user.id,
        rating,
        title: title.trim(),
        content: content.trim(),
        sentiment,
        status: reviewStatus,
        assignedToId,
        serviceDate: new Date(serviceDate),
        serviceType,
        subType: subType || null,
        notifiedAt: sentiment === 'NEGATIVE' ? new Date() : null,
        resolvedAt: sentiment === 'POSITIVE' ? new Date() : null,
      },
      include: {
        customer: { select: { id: true, name: true, email: true, company: true } },
        assignedTo: { select: { id: true, name: true } },
      },
    })

    // Send notifications for negative reviews - only to the assigned supervisor and managers
    if (sentiment === 'NEGATIVE') {
      // Notify managers
      const managers = await db.user.findMany({
        where: { role: 'MANAGER', isActive: true },
        select: { id: true },
      })
      if (managers.length > 0) {
        await notifyUsers({
          userIds: managers.map(m => m.id),
          title: 'Nueva reseña negativa',
          message: `El cliente ${review.customer.name}${review.customer.company ? ` (${review.customer.company})` : ''} ha registrado una reseña negativa (calificación: ${rating}). Código: ${reviewCode}`,
          type: 'NEGATIVE_REVIEW',
          reviewId: review.id,
        })
      }

      // Notify ONLY the assigned supervisor
      if (assignedToId) {
        await notifyUsers({
          userIds: [assignedToId],
          title: 'Reseña asignada',
          message: `Se le ha asignado la reseña ${reviewCode} para atención.`,
          type: 'NEGATIVE_REVIEW',
          reviewId: review.id,
        })
      }
    }

    // Save photos if any were uploaded
    if (photos.length > 0) {
      const allowedTypes = ['image/jpeg', 'image/png', 'image/webp', 'image/gif']
      const maxPhotos = 3
      const photosToSave = photos.slice(0, maxPhotos)

      for (const photo of photosToSave) {
        // Validate file type
        if (!allowedTypes.includes(photo.type)) continue
        // Validate file size (5MB max)
        if (photo.size > 5 * 1024 * 1024) continue

        const ext = photo.name.split('.').pop() || 'jpg'
        const filename = `${Date.now()}-${Math.random().toString(36).substring(2, 8)}.${ext}`
        const uploadDir = path.join(process.cwd(), 'public', 'uploads', 'reviews', review.id)
        const filePath = path.join(uploadDir, filename)

        await mkdir(uploadDir, { recursive: true })

        const bytes = await photo.arrayBuffer()
        const buffer = Buffer.from(bytes)
        await writeFile(filePath, buffer)

        const storageKey = `/uploads/reviews/${review.id}/${filename}`
        await db.reviewPhoto.create({
          data: {
            reviewId: review.id,
            storageKey,
            fileName: photo.name,
            sizeBytes: photo.size,
          },
        })
      }
    }

    return NextResponse.json({ review }, { status: 201 })
  } catch (error) {
    console.error('Error al crear reseña:', error)
    return NextResponse.json(
      { error: 'Error interno del servidor' },
      { status: 500 }
    )
  }
}
