import { db } from '../db/db_index'
import { deliverySlotInfo, productInfo, productReviews, specialDeals, storeInfo, units, users } from '../db/schema'
import { and, desc, eq, gt, sql } from 'drizzle-orm'
import type { UserProductDetailData, UserProductReview } from '@packages/shared'

const getStringArray = (value: unknown): string[] | null => {
  if (!Array.isArray(value)) return null
  return value.map((item) => String(item))
}

export async function getProductDetailById(productId: number): Promise<UserProductDetailData | null> {
  const productData = await db
    .select({
      id: productInfo.id,
      name: productInfo.name,
      shortDescription: productInfo.shortDescription,
      longDescription: productInfo.longDescription,
      price: productInfo.price,
      marketPrice: productInfo.marketPrice,
      images: productInfo.images,
      isOutOfStock: productInfo.isOutOfStock,
      storeId: productInfo.storeId,
      unitShortNotation: units.shortNotation,
      incrementStep: productInfo.incrementStep,
      productQuantity: productInfo.productQuantity,
      isFlashAvailable: productInfo.isFlashAvailable,
      flashPrice: productInfo.flashPrice,
    })
    .from(productInfo)
    .innerJoin(units, eq(productInfo.unitId, units.id))
    .where(eq(productInfo.id, productId))
    .limit(1)

  if (productData.length === 0) {
    return null
  }

  const product = productData[0]

  const storeData = product.storeId ? await db.query.storeInfo.findFirst({
    where: eq(storeInfo.id, product.storeId),
    columns: { id: true, name: true, description: true },
  }) : null

  // Note: deliverySlots are now fetched from cache in the frontend via useSlots()
  // This avoids expensive database joins on every product detail view
  const specialDealsData = await db
    .select({
      quantity: specialDeals.quantity,
      price: specialDeals.price,
      validTill: specialDeals.validTill,
    })
    .from(specialDeals)
    .where(
      and(
        eq(specialDeals.productId, productId),
        gt(specialDeals.validTill, sql`NOW()`)
      )
    )
    .orderBy(specialDeals.quantity)

  return {
    id: product.id,
    name: product.name,
    shortDescription: product.shortDescription ?? null,
    longDescription: product.longDescription ?? null,
    price: product.price.toString(),
    marketPrice: product.marketPrice?.toString() || null,
    unitNotation: product.unitShortNotation,
    images: getStringArray(product.images),
    isOutOfStock: product.isOutOfStock,
    store: storeData ? {
      id: storeData.id,
      name: storeData.name,
      description: storeData.description ?? null,
    } : null,
    incrementStep: product.incrementStep,
    productQuantity: product.productQuantity,
    isFlashAvailable: product.isFlashAvailable,
    flashPrice: product.flashPrice?.toString() || null,
    deliverySlots: [], // Fetched from cache in frontend via useSlots()
    specialDeals: specialDealsData.map((deal) => ({
      quantity: deal.quantity.toString(),
      price: deal.price.toString(),
      validTill: deal.validTill,
    })),
  }
}

export async function getProductReviews(productId: number, limit: number, offset: number) {
  const reviews = await db
    .select({
      id: productReviews.id,
      reviewBody: productReviews.reviewBody,
      ratings: productReviews.ratings,
      imageUrls: productReviews.imageUrls,
      reviewTime: productReviews.reviewTime,
      userName: users.name,
    })
    .from(productReviews)
    .innerJoin(users, eq(productReviews.userId, users.id))
    .where(eq(productReviews.productId, productId))
    .orderBy(desc(productReviews.reviewTime))
    .limit(limit)
    .offset(offset)

  const totalCountResult = await db
    .select({ count: sql`count(*)` })
    .from(productReviews)
    .where(eq(productReviews.productId, productId))

  const totalCount = Number(totalCountResult[0].count)

  const mappedReviews: UserProductReview[] = reviews.map((review) => ({
    id: review.id,
    reviewBody: review.reviewBody,
    ratings: review.ratings,
    imageUrls: getStringArray(review.imageUrls),
    reviewTime: review.reviewTime,
    userName: review.userName ?? null,
  }))

  return {
    reviews: mappedReviews,
    totalCount,
  }
}

export async function getProductById(productId: number) {
  return db.query.productInfo.findFirst({
    where: eq(productInfo.id, productId),
  })
}

export async function createProductReview(
  userId: number,
  productId: number,
  reviewBody: string,
  ratings: number,
  imageUrls: string[]
): Promise<UserProductReview> {
  const [newReview] = await db.insert(productReviews).values({
    userId,
    productId,
    reviewBody,
    ratings,
    imageUrls,
  }).returning({
    id: productReviews.id,
    reviewBody: productReviews.reviewBody,
    ratings: productReviews.ratings,
    imageUrls: productReviews.imageUrls,
    reviewTime: productReviews.reviewTime,
  })

  return {
    id: newReview.id,
    reviewBody: newReview.reviewBody,
    ratings: newReview.ratings,
    imageUrls: getStringArray(newReview.imageUrls),
    reviewTime: newReview.reviewTime,
    userName: null,
  }
}

export interface ProductSummaryData {
  id: number
  name: string
  shortDescription: string | null
  price: string
  marketPrice: string | null
  images: unknown
  isOutOfStock: boolean
  unitShortNotation: string
  productQuantity: number
}

export async function getAllProductsWithUnits(tagId?: number): Promise<ProductSummaryData[]> {
  let productIds: number[] | null = null

  // If tagId is provided, get products that have this tag
  if (tagId) {
    const taggedProducts = await db
      .select({ productId: productTags.productId })
      .from(productTags)
      .where(eq(productTags.tagId, tagId))

    productIds = taggedProducts.map(tp => tp.productId)
  }

  let whereCondition = undefined

  // Filter by product IDs if tag filtering is applied
  if (productIds && productIds.length > 0) {
    whereCondition = inArray(productInfo.id, productIds)
  }

  return db
    .select({
      id: productInfo.id,
      name: productInfo.name,
      shortDescription: productInfo.shortDescription,
      price: productInfo.price,
      marketPrice: productInfo.marketPrice,
      images: productInfo.images,
      isOutOfStock: productInfo.isOutOfStock,
      unitShortNotation: units.shortNotation,
      productQuantity: productInfo.productQuantity,
    })
    .from(productInfo)
    .innerJoin(units, eq(productInfo.unitId, units.id))
    .where(whereCondition)
}

/**
 * Get all suspended product IDs
 */
export async function getSuspendedProductIds(): Promise<number[]> {
  const suspendedProducts = await db
    .select({ id: productInfo.id })
    .from(productInfo)
    .where(eq(productInfo.isSuspended, true))

  return suspendedProducts.map(sp => sp.id)
}

/**
 * Get next delivery date for a product (with capacity check)
 * This version filters by both isActive AND isCapacityFull
 */
export async function getNextDeliveryDateWithCapacity(productId: number): Promise<Date | null> {
  const slots = await db.query.deliverySlotInfo.findMany({
    where: and(
      eq(deliverySlotInfo.isActive, true),
      eq(deliverySlotInfo.isCapacityFull, false),
      gt(deliverySlotInfo.deliveryTime, sql`NOW()`)
    ),
    orderBy: desc(deliverySlotInfo.deliveryTime),
  })

  // Find the first slot that contains this product
  for (const slot of slots) {
    const productIds = slot.productIds || []
    if (productIds.includes(productId)) {
      return slot.deliveryTime
    }
  }

  return null
}
