import { eq, and, count, desc, sql, type Column } from 'drizzle-orm'
import type { PostgresJsDatabase } from 'drizzle-orm/postgres-js'
import { products, inventoryUnits, priceHistory as priceHistoryTable, productSuppliers, suppliers, stockReceipts } from '../db/schema/inventory.js'
import { ValidationError } from '../lib/errors.js'
import type {
  ProductCreateInput,
  ProductUpdateInput,
  InventoryUnitCreateInput,
  InventoryUnitUpdateInput,
  ProductSupplierCreateInput,
  ProductSupplierUpdateInput,
  StockReceiptCreateInput,
  PaginationInput,
} from '@lunarfront/shared/schemas'
import {
  withPagination,
  withSort,
  buildSearchCondition,
  paginatedResponse,
} from '../utils/pagination.js'
import { UnitStatusService, ItemConditionService } from './lookup.service.js'

export const ProductService = {
  async create(db: PostgresJsDatabase<any>, input: ProductCreateInput) {
    const [product] = await db
      .insert(products)
      .values({
        ...input,
        price: input.price?.toString(),
        minPrice: input.minPrice?.toString(),
        rentalRateMonthly: input.rentalRateMonthly?.toString(),
      })
      .returning()
    return product
  },

  async getById(db: PostgresJsDatabase<any>, id: string) {
    const [product] = await db
      .select()
      .from(products)
      .where(eq(products.id, id))
      .limit(1)
    return product ?? null
  },

  async list(db: PostgresJsDatabase<any>, params: PaginationInput, filters?: {
    categoryId?: string
    isActive?: boolean
    isSerialized?: boolean
    isRental?: boolean
    isDualUseRepair?: boolean
    isConsumable?: boolean
    lowStock?: boolean
  }) {
    const conditions = [eq(products.isActive, filters?.isActive ?? true)]

    if (params.q) {
      conditions.push(buildSearchCondition(params.q, [products.name, products.sku, products.upc, products.brand])!)
    }
    if (filters?.categoryId) {
      conditions.push(eq(products.categoryId, filters.categoryId))
    }
    if (filters?.isSerialized !== undefined) {
      conditions.push(eq(products.isSerialized, filters.isSerialized))
    }
    if (filters?.isRental !== undefined) {
      conditions.push(eq(products.isRental, filters.isRental))
    }
    if (filters?.isDualUseRepair !== undefined) {
      conditions.push(eq(products.isDualUseRepair, filters.isDualUseRepair))
    }
    if (filters?.isConsumable !== undefined) {
      conditions.push(eq(products.isConsumable, filters.isConsumable))
    }
    if (filters?.lowStock) {
      // qty_on_hand <= qty_reorder_point (and reorder point is set) OR qty_on_hand = 0
      conditions.push(
        sql`(${products.qtyOnHand} = 0 OR (${products.qtyReorderPoint} IS NOT NULL AND ${products.qtyOnHand} <= ${products.qtyReorderPoint}))`
      )
    }

    const where = conditions.length === 1 ? conditions[0] : and(...conditions)

    const sortableColumns: Record<string, Column> = {
      name: products.name,
      sku: products.sku,
      brand: products.brand,
      price: products.price,
      created_at: products.createdAt,
    }

    let query = db.select().from(products).where(where).$dynamic()
    query = withSort(query, params.sort, params.order, sortableColumns, products.name)
    query = withPagination(query, params.page, params.limit)

    const [data, [{ total }]] = await Promise.all([
      query,
      db.select({ total: count() }).from(products).where(where),
    ])

    return paginatedResponse(data, total, params.page, params.limit)
  },

  async update(
    db: PostgresJsDatabase<any>,
    id: string,
    input: ProductUpdateInput,
    changedBy?: string,
  ) {
    if (input.price !== undefined || input.minPrice !== undefined) {
      const existing = await this.getById(db, id)
      if (existing) {
        await db.insert(priceHistoryTable).values({
          productId: id,
          previousPrice: existing.price,
          newPrice: input.price?.toString() ?? existing.price ?? '0',
          previousMinPrice: existing.minPrice,
          newMinPrice: input.minPrice?.toString() ?? existing.minPrice,
          changedBy,
        })
      }
    }

    const updates: Record<string, unknown> = { ...input, updatedAt: new Date() }
    if (input.price !== undefined) updates.price = input.price.toString()
    if (input.minPrice !== undefined) updates.minPrice = input.minPrice.toString()
    if (input.rentalRateMonthly !== undefined)
      updates.rentalRateMonthly = input.rentalRateMonthly.toString()

    const [product] = await db
      .update(products)
      .set(updates)
      .where(eq(products.id, id))
      .returning()
    return product ?? null
  },

  async softDelete(db: PostgresJsDatabase<any>, id: string) {
    const [product] = await db
      .update(products)
      .set({ isActive: false, updatedAt: new Date() })
      .where(eq(products.id, id))
      .returning()
    return product ?? null
  },

  async getByUpc(db: PostgresJsDatabase<any>, upc: string) {
    const [product] = await db
      .select()
      .from(products)
      .where(and(eq(products.upc, upc), eq(products.isActive, true)))
      .limit(1)
    return product ?? null
  },

  async listPriceHistory(db: PostgresJsDatabase<any>, productId: string) {
    return db
      .select()
      .from(priceHistoryTable)
      .where(eq(priceHistoryTable.productId, productId))
      .orderBy(desc(priceHistoryTable.createdAt))
  },
}

export const InventoryUnitService = {
  async create(db: PostgresJsDatabase<any>, input: InventoryUnitCreateInput) {
    if (input.condition) {
      const valid = await ItemConditionService.validateSlug(db, input.condition)
      if (!valid) throw new ValidationError(`Invalid condition: "${input.condition}"`)
    }
    if (input.status) {
      const valid = await UnitStatusService.validateSlug(db, input.status)
      if (!valid) throw new ValidationError(`Invalid status: "${input.status}"`)
    }

    const [unit] = await db
      .insert(inventoryUnits)
      .values({
        productId: input.productId,
        locationId: input.locationId,
        serialNumber: input.serialNumber,
        condition: input.condition,
        status: input.status,
        purchaseDate: input.purchaseDate,
        purchaseCost: input.purchaseCost?.toString(),
        notes: input.notes,
      })
      .returning()
    return unit
  },

  async getById(db: PostgresJsDatabase<any>, id: string) {
    const [unit] = await db
      .select()
      .from(inventoryUnits)
      .where(eq(inventoryUnits.id, id))
      .limit(1)
    return unit ?? null
  },

  async listByProduct(
    db: PostgresJsDatabase<any>,
    productId: string,
    params: PaginationInput,
  ) {
    const where = eq(inventoryUnits.productId, productId)

    const sortableColumns: Record<string, Column> = {
      serial_number: inventoryUnits.serialNumber,
      status: inventoryUnits.status,
      condition: inventoryUnits.condition,
      created_at: inventoryUnits.createdAt,
    }

    let query = db.select().from(inventoryUnits).where(where).$dynamic()
    query = withSort(query, params.sort, params.order, sortableColumns, inventoryUnits.createdAt)
    query = withPagination(query, params.page, params.limit)

    const [data, [{ total }]] = await Promise.all([
      query,
      db.select({ total: count() }).from(inventoryUnits).where(where),
    ])

    return paginatedResponse(data, total, params.page, params.limit)
  },

  async update(
    db: PostgresJsDatabase<any>,
    id: string,
    input: InventoryUnitUpdateInput,
  ) {
    if (input.condition) {
      const valid = await ItemConditionService.validateSlug(db, input.condition)
      if (!valid) throw new ValidationError(`Invalid condition: "${input.condition}"`)
    }
    if (input.status) {
      const valid = await UnitStatusService.validateSlug(db, input.status)
      if (!valid) throw new ValidationError(`Invalid status: "${input.status}"`)
    }

    const updates: Record<string, unknown> = { ...input, updatedAt: new Date() }
    if (input.purchaseCost !== undefined) updates.purchaseCost = input.purchaseCost.toString()

    const [unit] = await db
      .update(inventoryUnits)
      .set(updates)
      .where(eq(inventoryUnits.id, id))
      .returning()
    return unit ?? null
  },
}

export const ProductSupplierService = {
  async listByProduct(db: PostgresJsDatabase<any>, productId: string) {
    return db
      .select({
        id: productSuppliers.id,
        productId: productSuppliers.productId,
        supplierId: productSuppliers.supplierId,
        supplierSku: productSuppliers.supplierSku,
        isPreferred: productSuppliers.isPreferred,
        createdAt: productSuppliers.createdAt,
        supplierName: suppliers.name,
        supplierContactName: suppliers.contactName,
        supplierEmail: suppliers.email,
        supplierPhone: suppliers.phone,
        supplierAccountNumber: suppliers.accountNumber,
        supplierPaymentTerms: suppliers.paymentTerms,
      })
      .from(productSuppliers)
      .innerJoin(suppliers, eq(productSuppliers.supplierId, suppliers.id))
      .where(eq(productSuppliers.productId, productId))
      .orderBy(desc(productSuppliers.isPreferred), suppliers.name)
  },

  async create(db: PostgresJsDatabase<any>, productId: string, input: ProductSupplierCreateInput) {
    if (input.isPreferred) {
      await db
        .update(productSuppliers)
        .set({ isPreferred: false })
        .where(eq(productSuppliers.productId, productId))
    }
    const [row] = await db
      .insert(productSuppliers)
      .values({ productId, ...input })
      .returning()
    return row
  },

  async update(db: PostgresJsDatabase<any>, id: string, productId: string, input: ProductSupplierUpdateInput) {
    if (input.isPreferred) {
      await db
        .update(productSuppliers)
        .set({ isPreferred: false })
        .where(eq(productSuppliers.productId, productId))
    }
    const [row] = await db
      .update(productSuppliers)
      .set(input)
      .where(eq(productSuppliers.id, id))
      .returning()
    return row ?? null
  },

  async delete(db: PostgresJsDatabase<any>, id: string) {
    const [row] = await db
      .delete(productSuppliers)
      .where(eq(productSuppliers.id, id))
      .returning()
    return row ?? null
  },
}

export const StockReceiptService = {
  async create(db: PostgresJsDatabase<any>, productId: string, input: StockReceiptCreateInput) {
    const totalCost = (input.costPerUnit * input.qty).toFixed(2)

    const [receipt] = await db
      .insert(stockReceipts)
      .values({
        productId,
        supplierId: input.supplierId,
        inventoryUnitId: input.inventoryUnitId,
        qty: input.qty,
        costPerUnit: input.costPerUnit.toString(),
        totalCost,
        receivedDate: input.receivedDate,
        invoiceNumber: input.invoiceNumber,
        notes: input.notes,
      })
      .returning()

    // For non-serialized products, increment qty_on_hand atomically
    await db
      .update(products)
      .set({ qtyOnHand: sql`${products.qtyOnHand} + ${input.qty}`, updatedAt: new Date() })
      .where(and(eq(products.id, productId), eq(products.isSerialized, false)))

    return receipt
  },

  async listByProduct(db: PostgresJsDatabase<any>, productId: string) {
    return db
      .select({
        id: stockReceipts.id,
        productId: stockReceipts.productId,
        supplierId: stockReceipts.supplierId,
        inventoryUnitId: stockReceipts.inventoryUnitId,
        qty: stockReceipts.qty,
        costPerUnit: stockReceipts.costPerUnit,
        totalCost: stockReceipts.totalCost,
        receivedDate: stockReceipts.receivedDate,
        invoiceNumber: stockReceipts.invoiceNumber,
        notes: stockReceipts.notes,
        createdAt: stockReceipts.createdAt,
        supplierName: suppliers.name,
      })
      .from(stockReceipts)
      .leftJoin(suppliers, eq(stockReceipts.supplierId, suppliers.id))
      .where(eq(stockReceipts.productId, productId))
      .orderBy(desc(stockReceipts.receivedDate), desc(stockReceipts.createdAt))
  },
}
