import { db } from '../db/db_index'
import { addresses, deliverySlotInfo, orders, orderStatus } from '../db/schema'
import { and, eq, gte } from 'drizzle-orm'
import type { InferSelectModel } from 'drizzle-orm'
import type { UserAddress } from '@packages/shared'

type AddressRow = InferSelectModel<typeof addresses>

const mapUserAddress = (address: AddressRow): UserAddress => ({
  id: address.id,
  userId: address.userId,
  name: address.name,
  phone: address.phone,
  addressLine1: address.addressLine1,
  addressLine2: address.addressLine2 ?? null,
  city: address.city,
  state: address.state,
  pincode: address.pincode,
  isDefault: address.isDefault,
  latitude: address.latitude ?? null,
  longitude: address.longitude ?? null,
  googleMapsUrl: address.googleMapsUrl ?? null,
  adminLatitude: address.adminLatitude ?? null,
  adminLongitude: address.adminLongitude ?? null,
  zoneId: address.zoneId ?? null,
  createdAt: address.createdAt,
})

export async function getDefaultAddress(userId: number): Promise<UserAddress | null> {
  const [defaultAddress] = await db
    .select()
    .from(addresses)
    .where(and(eq(addresses.userId, userId), eq(addresses.isDefault, true)))
    .limit(1)

  return defaultAddress ? mapUserAddress(defaultAddress) : null
}

export async function getUserAddresses(userId: number): Promise<UserAddress[]> {
  const userAddresses = await db.select().from(addresses).where(eq(addresses.userId, userId))
  return userAddresses.map(mapUserAddress)
}

export async function getUserAddressById(userId: number, addressId: number): Promise<UserAddress | null> {
  const [address] = await db
    .select()
    .from(addresses)
    .where(and(eq(addresses.id, addressId), eq(addresses.userId, userId)))
    .limit(1)

  return address ? mapUserAddress(address) : null
}

export async function clearDefaultAddress(userId: number): Promise<void> {
  await db.update(addresses).set({ isDefault: false }).where(eq(addresses.userId, userId))
}

export async function createUserAddress(input: {
  userId: number
  name: string
  phone: string
  addressLine1: string
  addressLine2?: string
  city: string
  state: string
  pincode: string
  isDefault: boolean
  latitude?: number
  longitude?: number
  googleMapsUrl?: string
}): Promise<UserAddress> {
  const [newAddress] = await db.insert(addresses).values({
    userId: input.userId,
    name: input.name,
    phone: input.phone,
    addressLine1: input.addressLine1,
    addressLine2: input.addressLine2,
    city: input.city,
    state: input.state,
    pincode: input.pincode,
    isDefault: input.isDefault,
    latitude: input.latitude,
    longitude: input.longitude,
    googleMapsUrl: input.googleMapsUrl,
  }).returning()

  return mapUserAddress(newAddress)
}

export async function updateUserAddress(input: {
  userId: number
  addressId: number
  name: string
  phone: string
  addressLine1: string
  addressLine2?: string
  city: string
  state: string
  pincode: string
  isDefault: boolean
  latitude?: number
  longitude?: number
  googleMapsUrl?: string
}): Promise<UserAddress | null> {
  const [updatedAddress] = await db.update(addresses)
    .set({
      name: input.name,
      phone: input.phone,
      addressLine1: input.addressLine1,
      addressLine2: input.addressLine2,
      city: input.city,
      state: input.state,
      pincode: input.pincode,
      isDefault: input.isDefault,
      googleMapsUrl: input.googleMapsUrl,
      latitude: input.latitude,
      longitude: input.longitude,
    })
    .where(and(eq(addresses.id, input.addressId), eq(addresses.userId, input.userId)))
    .returning()

  return updatedAddress ? mapUserAddress(updatedAddress) : null
}

export async function deleteUserAddress(userId: number, addressId: number): Promise<boolean> {
  const [deleted] = await db.delete(addresses)
    .where(and(eq(addresses.id, addressId), eq(addresses.userId, userId)))
    .returning({ id: addresses.id })

  return !!deleted
}

export async function hasOngoingOrdersForAddress(addressId: number): Promise<boolean> {
  const ongoingOrders = await db.select({
    orderId: orders.id,
  })
    .from(orders)
    .innerJoin(orderStatus, eq(orders.id, orderStatus.orderId))
    .innerJoin(deliverySlotInfo, eq(orders.slotId, deliverySlotInfo.id))
    .where(and(
      eq(orders.addressId, addressId),
      eq(orderStatus.isCancelled, false),
      gte(deliverySlotInfo.deliveryTime, new Date())
    ))
    .limit(1)

  return ongoingOrders.length > 0
}
