import { db } from '@/src/db/db_index'
import {
  orders,
  orderItems,
  orderStatus,
  addresses,
  productInfo,
  paymentInfoTable,
  coupons,
  couponUsage,
  payments,
  cartItems,
  refunds,
  units,
  userDetails,
} from '@/src/db/schema'
import { eq, and, inArray, desc, gte } from 'drizzle-orm'

// ============ User/Auth Queries ============

/**
 * Get user details by user ID
 */
export async function getUserDetails(userId: number) {
  return db.query.userDetails.findFirst({
    where: eq(userDetails.userId, userId),
  })
}

// ============ Address Queries ============

/**
 * Get user address by ID
 */
export async function getUserAddress(userId: number, addressId: number) {
  return db.query.addresses.findFirst({
    where: and(eq(addresses.userId, userId), eq(addresses.id, addressId)),
  })
}

// ============ Product Queries ============

/**
 * Get product by ID
 */
export async function getProductById(productId: number) {
  return db.query.productInfo.findFirst({
    where: eq(productInfo.id, productId),
  })
}

/**
 * Get multiple products by IDs with unit info
 */
export async function getProductsByIdsWithUnits(productIds: number[]) {
  return db
    .select({
      id: productInfo.id,
      name: productInfo.name,
      shortDescription: productInfo.shortDescription,
      price: productInfo.price,
      images: productInfo.images,
      isOutOfStock: productInfo.isOutOfStock,
      unitShortNotation: units.shortNotation,
      incrementStep: productInfo.incrementStep,
    })
    .from(productInfo)
    .innerJoin(units, eq(productInfo.unitId, units.id))
    .where(and(inArray(productInfo.id, productIds), eq(productInfo.isSuspended, false)))
    .orderBy(desc(productInfo.createdAt))
}

// ============ Coupon Queries ============

/**
 * Get coupon with usages for user
 */
export async function getCouponWithUsages(couponId: number, userId: number) {
  return db.query.coupons.findFirst({
    where: eq(coupons.id, couponId),
    with: {
      usages: { where: eq(couponUsage.userId, userId) },
    },
  })
}

/**
 * Insert coupon usage
 */
export async function insertCouponUsage(data: {
  userId: number
  couponId: number
  orderId: number
  orderItemId: number | null
  usedAt: Date
}) {
  return db.insert(couponUsage).values(data)
}

/**
 * Get coupon usages for order
 */
export async function getCouponUsagesForOrder(orderId: number) {
  return db.query.couponUsage.findMany({
    where: eq(couponUsage.orderId, orderId),
    with: {
      coupon: true,
    },
  })
}

// ============ Cart Queries ============

/**
 * Delete cart items for user by product IDs
 */
export async function deleteCartItems(userId: number, productIds: number[]) {
  return db.delete(cartItems).where(
    and(
      eq(cartItems.userId, userId),
      inArray(cartItems.productId, productIds)
    )
  )
}

// ============ Payment Info Queries ============

/**
 * Create payment info
 */
export async function createPaymentInfo(data: {
  status: string
  gateway: string
  merchantOrderId: string
}) {
  return db.insert(paymentInfoTable).values(data).returning()
}



// ============ Order Queries ============

/**
 * Insert multiple orders
 */
export async function insertOrders(ordersData: any[]) {
  return db.insert(orders).values(ordersData).returning()
}

/**
 * Insert multiple order items
 */
export async function insertOrderItems(itemsData: any[]) {
  return db.insert(orderItems).values(itemsData)
}

/**
 * Insert multiple order statuses
 */
export async function insertOrderStatuses(statusesData: any[]) {
  return db.insert(orderStatus).values(statusesData)
}

/**
 * Get user orders with all relations
 */
export async function getUserOrdersWithRelations(userId: number, limit: number, offset: number) {
  return db.query.orders.findMany({
    where: eq(orders.userId, userId),
    with: {
      orderItems: {
        with: {
          product: true,
        },
      },
      slot: true,
      paymentInfo: true,
      orderStatus: true,
      refunds: true,
    },
    orderBy: (orders, { desc }) => [desc(orders.createdAt)],
    limit: limit,
    offset: offset,
  })
}

/**
 * Count user orders
 */
export async function countUserOrders(userId: number) {
  return db.$count(orders, eq(orders.userId, userId))
}

/**
 * Get order by ID with all relations
 */
export async function getOrderByIdWithRelations(orderId: number) {
  return db.query.orders.findFirst({
    where: eq(orders.id, orderId),
    with: {
      orderItems: {
        with: {
          product: true,
        },
      },
      slot: true,
      paymentInfo: true,
      orderStatus: {
        with: {
          refundCoupon: true,
        },
      },
      refunds: true,
    },
  })
}

/**
 * Get order by ID with order status
 */
export async function getOrderWithStatus(orderId: number) {
  return db.query.orders.findFirst({
    where: eq(orders.id, orderId),
    with: {
      orderStatus: true,
    },
  })
}

/**
 * Update order status to cancelled
 */
export async function updateOrderStatusToCancelled(
  statusId: number,
  data: {
    isCancelled: boolean
    cancelReason: string
    cancellationUserNotes: string
    cancellationReviewed: boolean
  }
) {
  return db
    .update(orderStatus)
    .set(data)
    .where(eq(orderStatus.id, statusId))
}

/**
 * Insert refund record
 */
export async function insertRefund(data: { orderId: number; refundStatus: string }) {
  return db.insert(refunds).values(data)
}

/**
 * Update order notes
 */
export async function updateOrderNotes(orderId: number, userNotes: string | null) {
  return db
    .update(orders)
    .set({ userNotes })
    .where(eq(orders.id, orderId))
}

/**
 * Get recent delivered orders for user
 */
export async function getRecentDeliveredOrders(
  userId: number,
  since: Date,
  limit: number
) {
  return db
    .select({ id: orders.id })
    .from(orders)
    .innerJoin(orderStatus, eq(orders.id, orderStatus.orderId))
    .where(
      and(
        eq(orders.userId, userId),
        eq(orderStatus.isDelivered, true),
        gte(orders.createdAt, since)
      )
    )
    .orderBy(desc(orders.createdAt))
    .limit(limit)
}

/**
 * Get order items by order IDs
 */
export async function getOrderItemsByOrderIds(orderIds: number[]) {
  return db
    .select({ productId: orderItems.productId })
    .from(orderItems)
    .where(inArray(orderItems.orderId, orderIds))
}

// ============ Transaction Helper ============

/**
 * Execute function within a database transaction
 */
export async function withTransaction<T>(fn: (tx: any) => Promise<T>): Promise<T> {
  return db.transaction(fn)
}

/**
 * Cancel order with refund record in a transaction
 */
export async function cancelOrderWithRefund(
  statusId: number,
  orderId: number,
  isCod: boolean,
  reason: string
): Promise<{ orderId: number }> {
  return db.transaction(async (tx) => {
    // Update order status
    await tx
      .update(orderStatus)
      .set({
        isCancelled: true,
        cancelReason: reason,
        cancellationUserNotes: reason,
        cancellationReviewed: false,
      })
      .where(eq(orderStatus.id, statusId))

    // Insert refund record
    const refundStatus = isCod ? "na" : "pending"
    await tx.insert(refunds).values({
      orderId,
      refundStatus,
    })

    return { orderId }
  })
}

type Tx = Parameters<Parameters<typeof db.transaction>[0]>[0]

/**
 * Create orders with payment info in a transaction
 */
export async function createOrdersWithPayment(
  ordersData: any[],
  paymentMethod: "online" | "cod",
  totalWithDelivery: number,
  razorpayOrderCreator?: (paymentInfoId: number, amount: string) => Promise<any>,
  paymentRecordInserter?: (paymentInfoId: number, razorpayOrder: any, tx: Tx) => Promise<any>
): Promise<typeof orders.$inferSelect[]> {
  return db.transaction(async (tx) => {
    let sharedPaymentInfoId: number | null = null
    if (paymentMethod === "online") {
      const [paymentInfo] = await tx
        .insert(paymentInfoTable)
        .values({
          status: "pending",
          gateway: "razorpay",
          merchantOrderId: `multi_order_${Date.now()}`,
        })
        .returning()
      sharedPaymentInfoId = paymentInfo.id
    }

    const ordersToInsert: Omit<typeof orders.$inferInsert, "id">[] = ordersData.map(
      (od) => ({
        ...od.order,
        paymentInfoId: sharedPaymentInfoId,
      })
    )

    const insertedOrders = await tx.insert(orders).values(ordersToInsert).returning()

    const allOrderItems: Omit<typeof orderItems.$inferInsert, "id">[] = []
    const allOrderStatuses: Omit<typeof orderStatus.$inferInsert, "id">[] = []

    insertedOrders.forEach((order: typeof orders.$inferSelect, index: number) => {
      const od = ordersData[index]
      od.orderItems.forEach((item: any) => {
        allOrderItems.push({ ...item, orderId: order.id as number })
      })
      allOrderStatuses.push({
        ...od.orderStatus,
        orderId: order.id as number,
      })
    })

    await tx.insert(orderItems).values(allOrderItems)
    await tx.insert(orderStatus).values(allOrderStatuses)

    if (paymentMethod === "online" && sharedPaymentInfoId && razorpayOrderCreator && paymentRecordInserter) {
      const razorpayOrder = await razorpayOrderCreator(
        sharedPaymentInfoId,
        totalWithDelivery.toString()
      )
      await paymentRecordInserter(
        sharedPaymentInfoId,
        razorpayOrder,
        tx
      )
    }

    return insertedOrders
  })
}
