import { db } from '../db/db_index'
import {
  orders,
  orderItems,
  orderStatus,
  addresses,
  productInfo,
  paymentInfoTable,
  coupons,
  couponUsage,
  cartItems,
  refunds,
  units,
  userDetails,
  deliverySlotInfo,
} from '../db/schema'
import { and, eq, inArray, desc, gte, lte } from 'drizzle-orm'
import type {
  UserOrderSummary,
  UserOrderDetail,
  UserRecentProduct,
} from '@packages/shared'

export interface OrderItemInput {
  productId: number
  quantity: number
  slotId: number | null
}

export interface PlaceOrderInput {
  userId: number
  selectedItems: OrderItemInput[]
  addressId: number
  paymentMethod: 'online' | 'cod'
  couponId?: number
  userNotes?: string
  isFlash?: boolean
}

export interface OrderGroupData {
  slotId: number | null
  items: Array<{
    productId: number
    quantity: number
    slotId: number | null
    product: typeof productInfo.$inferSelect
  }>
}

export interface PlacedOrder {
  id: number
  userId: number
  addressId: number
  slotId: number | null
  totalAmount: string
  deliveryCharge: string
  isCod: boolean
  isOnlinePayment: boolean
  paymentInfoId: number | null
  readableId: number
  userNotes: string | null
  orderGroupId: string
  orderGroupProportion: string
  isFlashDelivery: boolean
  createdAt: Date
}

export interface OrderWithRelations {
  id: number
  userId: number
  addressId: number
  slotId: number | null
  totalAmount: string
  deliveryCharge: string
  isCod: boolean
  isOnlinePayment: boolean
  isFlashDelivery: boolean
  userNotes: string | null
  createdAt: Date
  orderItems: Array<{
    id: number
    productId: number
    quantity: string
    price: string
    discountedPrice: string | null
    is_packaged: boolean
    product: {
      id: number
      name: string
      images: unknown
    }
  }>
  slot: {
    deliveryTime: Date
  } | null
  paymentInfo: {
    id: number
    status: string
  } | null
  orderStatus: Array<{
    id: number
    isCancelled: boolean
    isDelivered: boolean
    paymentStatus: string
    cancelReason: string | null
  }>
  refunds: Array<{
    refundStatus: string
    refundAmount: string | null
  }>
}

export interface OrderDetailWithRelations {
  id: number
  userId: number
  addressId: number
  slotId: number | null
  totalAmount: string
  deliveryCharge: string
  isCod: boolean
  isOnlinePayment: boolean
  isFlashDelivery: boolean
  userNotes: string | null
  createdAt: Date
  orderItems: Array<{
    id: number
    productId: number
    quantity: string
    price: string
    discountedPrice: string | null
    is_packaged: boolean
    product: {
      id: number
      name: string
      images: unknown
    }
  }>
  slot: {
    deliveryTime: Date
  } | null
  paymentInfo: {
    id: number
    status: string
  } | null
  orderStatus: Array<{
    id: number
    isCancelled: boolean
    isDelivered: boolean
    paymentStatus: string
    cancelReason: string | null
  }>
  refunds: Array<{
    refundStatus: string
    refundAmount: string | null
  }>
}

export interface CouponValidationResult {
  id: number
  couponCode: string
  isInvalidated: boolean
  validTill: Date | null
  maxLimitForUser: number | null
  minOrder: string | null
  discountPercent: string | null
  flatDiscount: string | null
  maxValue: string | null
  usages: Array<{
    id: number
    userId: number
  }>
}

export interface CouponUsageWithCoupon {
  id: number
  couponId: number
  orderId: number | null
  coupon: {
    id: number
    couponCode: string
    discountPercent: string | null
    flatDiscount: string | null
    maxValue: string | null
  }
}

export async function validateAndGetCoupon(
  couponId: number | undefined,
  userId: number,
  totalAmount: number
): Promise<CouponValidationResult | null> {
  if (!couponId) return null

  const coupon = await db.query.coupons.findFirst({
    where: eq(coupons.id, couponId),
    with: {
      usages: { where: eq(couponUsage.userId, userId) },
    },
  })

  if (!coupon) throw new Error('Invalid coupon')
  if (coupon.isInvalidated) throw new Error('Coupon is no longer valid')
  if (coupon.validTill && new Date(coupon.validTill) < new Date())
    throw new Error('Coupon has expired')
  if (
    coupon.maxLimitForUser &&
    coupon.usages.length >= coupon.maxLimitForUser
  )
    throw new Error('Coupon usage limit exceeded')
  if (
    coupon.minOrder &&
    parseFloat(coupon.minOrder.toString()) > totalAmount
  )
    throw new Error('Order amount does not meet coupon minimum requirement')

  return coupon as CouponValidationResult
}

export function applyDiscountToOrder(
  orderTotal: number,
  appliedCoupon: CouponValidationResult | null,
  proportion: number
): { finalOrderTotal: number; orderGroupProportion: number } {
  let finalOrderTotal = orderTotal
  
  if (appliedCoupon) {
    if (appliedCoupon.discountPercent) {
      const discount = Math.min(
        (orderTotal *
          parseFloat(appliedCoupon.discountPercent.toString())) /
        100,
        appliedCoupon.maxValue
          ? parseFloat(appliedCoupon.maxValue.toString()) * proportion
          : Infinity
      )
      finalOrderTotal -= discount
    } else if (appliedCoupon.flatDiscount) {
      const discount = Math.min(
        parseFloat(appliedCoupon.flatDiscount.toString()) * proportion,
        appliedCoupon.maxValue
          ? parseFloat(appliedCoupon.maxValue.toString()) * proportion
          : finalOrderTotal
      )
      finalOrderTotal -= discount
    }
  }

  return { finalOrderTotal, orderGroupProportion: proportion }
}

export async function getAddressByIdAndUser(
  addressId: number,
  userId: number
) {
  return db.query.addresses.findFirst({
    where: and(eq(addresses.userId, userId), eq(addresses.id, addressId)),
  })
}

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

export async function checkUserSuspended(userId: number): Promise<boolean> {
  const userDetail = await db.query.userDetails.findFirst({
    where: eq(userDetails.userId, userId),
  })
  return userDetail?.isSuspended ?? false
}

export async function getSlotCapacityStatus(slotId: number): Promise<boolean> {
  const slot = await db.query.deliverySlotInfo.findFirst({
    where: eq(deliverySlotInfo.id, slotId),
    columns: {
      isCapacityFull: true,
    },
  })
  return slot?.isCapacityFull ?? false
}

export async function placeOrderTransaction(params: {
  userId: number
  ordersData: Array<{
    order: Omit<typeof orders.$inferInsert, 'id'>
    orderItems: Omit<typeof orderItems.$inferInsert, 'id'>[]
    orderStatus: Omit<typeof orderStatus.$inferInsert, 'id'>
  }>
  paymentMethod: 'online' | 'cod'
  totalWithDelivery: number
}): Promise<PlacedOrder[]> {
  const { userId, ordersData, paymentMethod } = params

  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, index) => {
      const od = ordersData[index]
      od.orderItems.forEach((item) => {
        allOrderItems.push({ ...item, orderId: order.id })
      })
      allOrderStatuses.push({
        ...od.orderStatus,
        orderId: order.id,
      })
    })

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

    return insertedOrders as PlacedOrder[]
  })
}

export async function deleteCartItemsForOrder(
  userId: number,
  productIds: number[]
): Promise<void> {
  await db.delete(cartItems).where(
    and(
      eq(cartItems.userId, userId),
      inArray(cartItems.productId, productIds)
    )
  )
}

export async function recordCouponUsage(
  userId: number,
  couponId: number,
  orderId: number
): Promise<void> {
  await db.insert(couponUsage).values({
    userId,
    couponId,
    orderId,
    orderItemId: null,
    usedAt: new Date(),
  })
}

export async function getOrdersWithRelations(
  userId: number,
  offset: number,
  pageSize: number
): Promise<OrderWithRelations[]> {
  return db.query.orders.findMany({
    where: eq(orders.userId, userId),
    with: {
      orderItems: {
        with: {
          product: {
            columns: {
              id: true,
              name: true,
              images: true,
            },
          },
        },
      },
      slot: {
        columns: {
          deliveryTime: true,
        },
      },
      paymentInfo: {
        columns: {
          id: true,
          status: true,
        },
      },
      orderStatus: {
        columns: {
          id: true,
          isCancelled: true,
          isDelivered: true,
          paymentStatus: true,
          cancelReason: true,
        },
      },
      refunds: {
        columns: {
          refundStatus: true,
          refundAmount: true,
        },
      },
    },
    orderBy: (orders, { desc }) => [desc(orders.createdAt)],
    limit: pageSize,
    offset: offset,
  }) as Promise<OrderWithRelations[]>
}

export async function getOrderCount(userId: number): Promise<number> {
  const result = await db
    .select({ count: db.$count(orders, eq(orders.userId, userId)) })
    .from(orders)
    .where(eq(orders.userId, userId))
  
  return result[0]?.count ?? 0
}

export async function getOrderByIdWithRelations(
  orderId: number,
  userId: number
): Promise<OrderDetailWithRelations | null> {
  const order = await db.query.orders.findFirst({
    where: and(eq(orders.id, orderId), eq(orders.userId, userId)),
    with: {
      orderItems: {
        with: {
          product: {
            columns: {
              id: true,
              name: true,
              images: true,
            },
          },
        },
      },
      slot: {
        columns: {
          deliveryTime: true,
        },
      },
      paymentInfo: {
        columns: {
          id: true,
          status: true,
        },
      },
      orderStatus: {
        columns: {
          id: true,
          isCancelled: true,
          isDelivered: true,
          paymentStatus: true,
          cancelReason: true,
        },
        with: {
          refundCoupon: {
            columns: {
              id: true,
              couponCode: true,
            },
          },
        },
      },
      refunds: {
        columns: {
          refundStatus: true,
          refundAmount: true,
        },
      },
    },
  })

  return order as OrderDetailWithRelations | null
}

export async function getCouponUsageForOrder(
  orderId: number
): Promise<CouponUsageWithCoupon[]> {
  return db.query.couponUsage.findMany({
    where: eq(couponUsage.orderId, orderId),
    with: {
      coupon: {
        columns: {
          id: true,
          couponCode: true,
          discountPercent: true,
          flatDiscount: true,
          maxValue: true,
        },
      },
    },
  }) as Promise<CouponUsageWithCoupon[]>
}

export async function getOrderBasic(orderId: number) {
  return db.query.orders.findFirst({
    where: eq(orders.id, orderId),
    with: {
      orderStatus: {
        columns: {
          id: true,
          isCancelled: true,
          isDelivered: true,
        },
      },
    },
  })
}

export async function cancelOrderTransaction(
  orderId: number,
  statusId: number,
  reason: string,
  isCod: boolean
): Promise<void> {
  await db.transaction(async (tx) => {
    await tx
      .update(orderStatus)
      .set({
        isCancelled: true,
        cancelReason: reason,
        cancellationUserNotes: reason,
        cancellationReviewed: false,
      })
      .where(eq(orderStatus.id, statusId))

    const refundStatus = isCod ? 'na' : 'pending'

    await tx.insert(refunds).values({
      orderId,
      refundStatus,
    })
  })
}

export async function updateOrderNotes(
  orderId: number,
  userNotes: string
): Promise<void> {
  await db
    .update(orders)
    .set({
      userNotes: userNotes || null,
    })
    .where(eq(orders.id, orderId))
}

export async function getRecentlyDeliveredOrderIds(
  userId: number,
  limit: number,
  since: Date
): Promise<number[]> {
  const recentOrders = await 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)

  return recentOrders.map((order) => order.id)
}

export async function getProductIdsFromOrders(
  orderIds: number[]
): Promise<number[]> {
  const orderItemsResult = await db
    .select({ productId: orderItems.productId })
    .from(orderItems)
    .where(inArray(orderItems.orderId, orderIds))

  return [...new Set(orderItemsResult.map((item) => item.productId))]
}

export interface RecentProductData {
  id: number
  name: string
  shortDescription: string | null
  price: string
  images: unknown
  isOutOfStock: boolean
  unitShortNotation: string
  incrementStep: number
}

export async function getProductsForRecentOrders(
  productIds: number[],
  limit: number
): Promise<RecentProductData[]> {
  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))
    .limit(limit)
}

// ============================================================================
// Post-Order Handler Helpers (for Telegram notifications)
// ============================================================================

export interface OrderWithFullData {
  id: number
  userId: number
  totalAmount: string
  isFlashDelivery: boolean
  address: {
    name: string | null
    addressLine1: string | null
    addressLine2: string | null
    city: string | null
    state: string | null
    pincode: string | null
    phone: string | null
  } | null
  orderItems: Array<{
    quantity: string
    product: {
      name: string
    } | null
  }>
  slot: {
    deliveryTime: Date
  } | null
}

export async function getOrdersByIdsWithFullData(
  orderIds: number[]
): Promise<OrderWithFullData[]> {
  return db.query.orders.findMany({
    where: inArray(orders.id, orderIds),
    with: {
      address: {
        columns: {
          name: true,
          addressLine1: true,
          addressLine2: true,
          city: true,
          state: true,
          pincode: true,
          phone: true,
        },
      },
      orderItems: {
        with: {
          product: {
            columns: {
              name: true,
            },
          },
        },
      },
      slot: {
        columns: {
          deliveryTime: true,
        },
      },
    },
  }) as Promise<OrderWithFullData[]>
}

export interface OrderWithCancellationData extends OrderWithFullData {
  refunds: Array<{
    refundStatus: string
  }>
}

export async function getOrderByIdWithFullData(
  orderId: number
): Promise<OrderWithCancellationData | null> {
  return db.query.orders.findFirst({
    where: eq(orders.id, orderId),
    with: {
      address: {
        columns: {
          name: true,
          addressLine1: true,
          addressLine2: true,
          city: true,
          state: true,
          pincode: true,
          phone: true,
        },
      },
      orderItems: {
        with: {
          product: {
            columns: {
              name: true,
            },
          },
        },
      },
      slot: {
        columns: {
          deliveryTime: true,
        },
      },
      refunds: {
        columns: {
          refundStatus: true,
        },
      },
    },
  }) as Promise<OrderWithCancellationData | null>
}
