import { db } from '../db/db_index'
import { vendorSnippets, deliverySlotInfo, productInfo, orders, orderItems, orderStatus } from '../db/schema'
import { desc, eq, inArray } from 'drizzle-orm'
import type { InferSelectModel } from 'drizzle-orm'
import type {
  AdminDeliverySlot,
  AdminVendorSnippet,
  AdminVendorSnippetWithSlot,
  AdminVendorSnippetProduct,
  AdminVendorUpdatePackagingResult,
} from '@packages/shared'

type VendorSnippetRow = InferSelectModel<typeof vendorSnippets>
type DeliverySlotRow = InferSelectModel<typeof deliverySlotInfo>
type ProductRow = InferSelectModel<typeof productInfo>

const mapVendorSnippet = (snippet: VendorSnippetRow): AdminVendorSnippet => ({
  id: snippet.id,
  snippetCode: snippet.snippetCode,
  slotId: snippet.slotId ?? null,
  productIds: snippet.productIds,
  isPermanent: snippet.isPermanent,
  validTill: snippet.validTill ?? null,
  createdAt: snippet.createdAt,
})

const mapDeliverySlot = (slot: DeliverySlotRow): AdminDeliverySlot => ({
  id: slot.id,
  deliveryTime: slot.deliveryTime,
  freezeTime: slot.freezeTime,
  isActive: slot.isActive,
  isFlash: slot.isFlash,
  isCapacityFull: slot.isCapacityFull,
  deliverySequence: slot.deliverySequence,
  groupIds: slot.groupIds,
})

const mapProductSummary = (product:{id:number, name: string}): AdminVendorSnippetProduct => ({
  id: product.id,
  name: product.name,
})

export async function checkVendorSnippetExists(snippetCode: string): Promise<boolean> {
  const existingSnippet = await db.query.vendorSnippets.findFirst({
    where: eq(vendorSnippets.snippetCode, snippetCode),
  })
  return !!existingSnippet
}

export async function getVendorSnippetById(id: number): Promise<AdminVendorSnippetWithSlot | null> {
  const snippet = await db.query.vendorSnippets.findFirst({
    where: eq(vendorSnippets.id, id),
    with: {
      slot: true,
    },
  })

  if (!snippet) {
    return null
  }

  return {
    ...mapVendorSnippet(snippet),
    slot: snippet.slot ? mapDeliverySlot(snippet.slot) : null,
  }
}

export async function getVendorSnippetByCode(snippetCode: string): Promise<AdminVendorSnippet | null> {
  const snippet = await db.query.vendorSnippets.findFirst({
    where: eq(vendorSnippets.snippetCode, snippetCode),
  })

  return snippet ? mapVendorSnippet(snippet) : null
}

export async function getAllVendorSnippets(): Promise<AdminVendorSnippetWithSlot[]> {
  const snippets = await db.query.vendorSnippets.findMany({
    with: {
      slot: true,
    },
    orderBy: (vendorSnippets, { desc }) => [desc(vendorSnippets.createdAt)],
  })

  return snippets.map((snippet) => ({
    ...mapVendorSnippet(snippet),
    slot: snippet.slot ? mapDeliverySlot(snippet.slot) : null,
  }))
}

export async function createVendorSnippet(input: {
  snippetCode: string
  slotId?: number
  productIds: number[]
  isPermanent: boolean
  validTill?: Date
}): Promise<AdminVendorSnippet> {
  const [result] = await db.insert(vendorSnippets).values({
    snippetCode: input.snippetCode,
    slotId: input.slotId,
    productIds: input.productIds,
    isPermanent: input.isPermanent,
    validTill: input.validTill,
  }).returning()

  return mapVendorSnippet(result)
}

export async function updateVendorSnippet(id: number, updates: {
  snippetCode?: string
  slotId?: number | null
  productIds?: number[]
  isPermanent?: boolean
  validTill?: Date | null
}): Promise<AdminVendorSnippet | null> {
  const [result] = await db.update(vendorSnippets)
    .set(updates)
    .where(eq(vendorSnippets.id, id))
    .returning()

  return result ? mapVendorSnippet(result) : null
}

export async function deleteVendorSnippet(id: number): Promise<AdminVendorSnippet | null> {
  const [result] = await db.delete(vendorSnippets)
    .where(eq(vendorSnippets.id, id))
    .returning()

  return result ? mapVendorSnippet(result) : null
}

export async function getProductsByIds(productIds: number[]): Promise<AdminVendorSnippetProduct[]> {
  const products = await db.query.productInfo.findMany({
    where: inArray(productInfo.id, productIds),
    columns: { id: true, name: true },
  })

  const prods = products.map(mapProductSummary)
  return prods;
}

export async function getVendorSlotById(slotId: number): Promise<AdminDeliverySlot | null> {
  const slot = await db.query.deliverySlotInfo.findFirst({
    where: eq(deliverySlotInfo.id, slotId),
  })

  return slot ? mapDeliverySlot(slot) : null
}

export async function getVendorOrdersBySlotId(slotId: number) {
  return await db.query.orders.findMany({
    where: eq(orders.slotId, slotId),
    with: {
      orderItems: {
        with: {
          product: {
            with: {
              unit: true,
            },
          },
        },
      },
      orderStatus: true,
      user: true,
      slot: true,
    },
    orderBy: (orders, { desc }) => [desc(orders.createdAt)],
  })
}

export async function getVendorOrders() {
  return await db.query.orders.findMany({
    with: {
      user: true,
      orderItems: {
        with: {
          product: {
            with: {
              unit: true,
            },
          },
        },
      },
    },
    orderBy: (orders, { desc }) => [desc(orders.createdAt)],
  })
}

export async function getOrderItemsByOrderIds(orderIds: number[]) {
  return await db.query.orderItems.findMany({
    where: inArray(orderItems.orderId, orderIds),
    with: {
      product: {
        with: {
          unit: true,
        },
      },
    },
  })
}

export async function getOrderStatusByOrderIds(orderIds: number[]) {
  return await db.query.orderStatus.findMany({
    where: inArray(orderStatus.orderId, orderIds),
  })
}

export async function updateVendorOrderItemPackaging(
  orderItemId: number,
  isPackaged: boolean
): Promise<AdminVendorUpdatePackagingResult> {
  const orderItem = await db.query.orderItems.findFirst({
    where: eq(orderItems.id, orderItemId),
    with: {
      order: {
        with: {
          slot: true,
        },
      },
    },
  })

  if (!orderItem) {
    return { success: false, message: 'Order item not found' }
  }

  if (!orderItem.order.slotId) {
    return { success: false, message: 'Order item not associated with a vendor slot' }
  }

  const snippetExists = await db.query.vendorSnippets.findFirst({
    where: eq(vendorSnippets.slotId, orderItem.order.slotId),
  })

  if (!snippetExists) {
    return { success: false, message: "No vendor snippet found for this order's slot" }
  }

  const [updatedItem] = await db.update(orderItems)
    .set({
      is_packaged: isPackaged,
    })
    .where(eq(orderItems.id, orderItemId))
    .returning({ id: orderItems.id })

  if (!updatedItem) {
    return { success: false, message: 'Failed to update packaging status' }
  }

  return { success: true, orderItemId, is_packaged: isPackaged }
}
