import { prisma } from '../../lib/prisma.js'

const PUBLIC_PRODUCTS_QUERY_SCHEMA = {
  querystring: {
    type: 'object',
    properties: {
      categorySlug: { type: 'string' },
      q: { type: 'string' },
      sort: { type: 'string', enum: ['', 'price_asc', 'price_desc'] },
      page: { type: 'integer', minimum: 1 },
      pageSize: { type: 'integer', minimum: 1, maximum: 100 },
      priceMin: { type: 'number', minimum: 0 },
      priceMax: { type: 'number', minimum: 0 },
    },
  },
}

const EMPTY_REVIEWS_SUMMARY = Object.freeze({
  approvedReviewCount: 0,
  avgRating: null,
  latestApprovedText: null,
})

/** Сводка по одобренным отзывам для списка id товаров (для каталога и карточки товара). */
export async function approvedReviewSummariesForProducts(productIds) {
  const map = new Map()
  if (!productIds.length) return map

  const uniqueIds = [...new Set(productIds)]
  for (const id of uniqueIds) {
    map.set(id, { ...EMPTY_REVIEWS_SUMMARY })
  }

  const grouped = await prisma.review.groupBy({
    by: ['productId'],
    where: { productId: { in: uniqueIds }, status: 'approved' },
    _count: { _all: true },
    _avg: { rating: true },
  })

  for (const g of grouped) {
    const avg = g._avg.rating
    const prev = map.get(g.productId)
    if (!prev) continue
    map.set(g.productId, {
      ...prev,
      approvedReviewCount: g._count._all,
      avgRating: avg != null ? Number(avg) : null,
    })
  }

  const withReviews = [...map.entries()].filter(([, v]) => v.approvedReviewCount > 0).map(([k]) => k)
  if (!withReviews.length) return map

  const previewRows = await prisma.review.findMany({
    where: { productId: { in: withReviews }, status: 'approved' },
    orderBy: { createdAt: 'desc' },
    select: { productId: true, text: true },
    take: 450,
  })
  const hasPreviewFor = new Set()
  for (const r of previewRows) {
    if (hasPreviewFor.has(r.productId)) continue
    const t = typeof r.text === 'string' ? r.text.trim() : ''
    if (!t) continue
    hasPreviewFor.add(r.productId)
    const prev = map.get(r.productId)
    if (!prev) continue
    prev.latestApprovedText = t.length > 160 ? `${t.slice(0, 160)}…` : t
    if (hasPreviewFor.size === withReviews.length) break
  }

  return map
}

export async function registerPublicCatalogRoutes(fastify) {
  fastify.get('/api/categories', async () => {
    return prisma.category.findMany({ orderBy: { sort: 'asc' } })
  })

  fastify.get('/api/products', { schema: PUBLIC_PRODUCTS_QUERY_SCHEMA }, async (request, reply) => {
    const { mapProductForApi } = request.server
    const { categorySlug } = request.query
    const qRaw = request.query?.q
    const q = typeof qRaw === 'string' ? qRaw.trim() : ''

    const sortRaw = request.query?.sort
    const sort = typeof sortRaw === 'string' ? sortRaw : ''

    const pageRaw = request.query?.page
    const pageParsed = typeof pageRaw === 'string' ? Number(pageRaw) : Number(pageRaw)
    const page = Number.isFinite(pageParsed) && pageParsed > 0 ? Math.floor(pageParsed) : 1

    const pageSizeRaw = request.query?.pageSize
    const pageSizeParsed = typeof pageSizeRaw === 'string' ? Number(pageSizeRaw) : Number(pageSizeRaw)
    const pageSize = Number.isFinite(pageSizeParsed) && pageSizeParsed > 0 ? Math.floor(pageSizeParsed) : 12

    const priceMinRaw = request.query?.priceMin
    const priceMinParsed = typeof priceMinRaw === 'string' ? Number(priceMinRaw) : Number(priceMinRaw)
    const priceMin = Number.isFinite(priceMinParsed) && priceMinParsed >= 0 ? Math.floor(priceMinParsed) : null

    const priceMaxRaw = request.query?.priceMax
    const priceMaxParsed = typeof priceMaxRaw === 'string' ? Number(priceMaxRaw) : Number(priceMaxRaw)
    const priceMax = Number.isFinite(priceMaxParsed) && priceMaxParsed >= 0 ? Math.floor(priceMaxParsed) : null

    const where = { published: true }
    if (typeof categorySlug === 'string' && categorySlug.length > 0) {
      where.category = { slug: categorySlug }
    }
    if (q) {
      where.OR = [{ title: { contains: q } }, { shortDescription: { contains: q } }]
    }
    const applyPriceFilter = !(priceMin !== null && priceMax !== null && priceMin === 0 && priceMax === 0)

    if (applyPriceFilter && (priceMin !== null || priceMax !== null)) {
      if (priceMin !== null && priceMax !== null && priceMax < priceMin) {
        return reply.code(400).send({ error: 'priceMax должен быть ≥ priceMin' })
      }
      where.priceCents = {
        ...(priceMin !== null ? { gte: priceMin } : {}),
        ...(priceMax !== null ? { lte: priceMax } : {}),
      }
    }

    const orderBy =
      sort === 'price_asc'
        ? { priceCents: 'asc' }
        : sort === 'price_desc'
          ? { priceCents: 'desc' }
          : { createdAt: 'desc' }

    const total = await prisma.product.count({ where })
    const items = await prisma.product.findMany({
      where,
      include: { category: true, images: { orderBy: { sort: 'asc' } } },
      orderBy,
      skip: (page - 1) * pageSize,
      take: pageSize,
    })

    const summaries = await approvedReviewSummariesForProducts(items.map((it) => it.id))
    return {
      items: items.map((p) => request.server.mapProductForApi(p, summaries.get(p.id) ?? EMPTY_REVIEWS_SUMMARY)),
      total,
      page,
      pageSize,
    }
  })

  fastify.get('/api/products/:id', async (request, reply) => {
    const { id } = request.params
    const product = await prisma.product.findFirst({
      where: { id, published: true },
      include: { category: true, images: { orderBy: { sort: 'asc' } } },
    })
    if (!product) {
      reply.code(404).send({ error: 'Товар не найден' })
      return
    }
    const summaries = await approvedReviewSummariesForProducts([product.id])
    return request.server.mapProductForApi(product, summaries.get(product.id) ?? EMPTY_REVIEWS_SUMMARY)
  })
}

