import { router, protectedProcedure, publicProcedure } from '@/src/trpc/trpc-index'
import { z } from 'zod'
import { ApiError } from '@/src/lib/api-error'
import { scaffoldAssetUrl } from '@/src/lib/s3-client'
import { getMultipleProductsSlots } from '@/src/stores/slot-store'
import {
  getUserCartItemsWithProducts as getUserCartItemsWithProductsInDb,
  getUserProductById as getUserProductByIdInDb,
  getUserCartItemByUserProduct as getUserCartItemByUserProductInDb,
  incrementUserCartItemQuantity as incrementUserCartItemQuantityInDb,
  insertUserCartItem as insertUserCartItemInDb,
  updateUserCartItemQuantity as updateUserCartItemQuantityInDb,
  deleteUserCartItem as deleteUserCartItemInDb,
  clearUserCart as clearUserCartInDb,
} from '@/src/dbService'
import type { UserCartResponse } from '@packages/shared'

const getCartData = async (userId: number): Promise<UserCartResponse> => {
  const cartItemsWithProducts = await getUserCartItemsWithProductsInDb(userId)

  /*
  // Old implementation - direct DB queries:
  const cartItemsWithProducts = await db
    .select({
      cartId: cartItems.id,
      productId: productInfo.id,
      productName: productInfo.name,
      productPrice: productInfo.price,
      productImages: productInfo.images,
      productQuantity: productInfo.productQuantity,
      isOutOfStock: productInfo.isOutOfStock,
      unitShortNotation: units.shortNotation,
      quantity: cartItems.quantity,
      addedAt: cartItems.addedAt,
    })
    .from(cartItems)
    .innerJoin(productInfo, eq(cartItems.productId, productInfo.id))
    .innerJoin(units, eq(productInfo.unitId, units.id))
    .where(eq(cartItems.userId, userId));
  */

  const cartWithSignedUrls = cartItemsWithProducts.map((item) => ({
    ...item,
    product: {
      ...item.product,
      images: scaffoldAssetUrl(item.product.images || []),
    },
  }))

  const totalAmount = cartWithSignedUrls.reduce((sum, item) => sum + item.subtotal, 0)

  return {
    items: cartWithSignedUrls,
    totalItems: cartWithSignedUrls.length,
    totalAmount,
  }
}

export const cartRouter = router({
  getCart: protectedProcedure
    .query(async ({ ctx }): Promise<UserCartResponse> => {
      const userId = ctx.user.userId;
      return await getCartData(userId);
    }),

  addToCart: protectedProcedure
    .input(z.object({
      productId: z.number().int().positive(),
      quantity: z.number().int().positive(),
    }))
    .mutation(async ({ input, ctx }): Promise<UserCartResponse> => {
      const userId = ctx.user.userId;
      const { productId, quantity } = input;

      // Validate input
      if (!productId || !quantity || quantity <= 0) {
        throw new ApiError("Product ID and positive quantity required", 400);
      }

      // Check if product exists
      const product = await getUserProductByIdInDb(productId)

      if (!product) {
        throw new ApiError('Product not found', 404)
      }

      const existingItem = await getUserCartItemByUserProductInDb(userId, productId)

      if (existingItem) {
        await incrementUserCartItemQuantityInDb(existingItem.id, quantity)
      } else {
        await insertUserCartItemInDb(userId, productId, quantity)
      }

      /*
      // Old implementation - direct DB queries:
      const product = await db.query.productInfo.findFirst({
        where: eq(productInfo.id, productId),
      });

      if (!product) {
        throw new ApiError("Product not found", 404);
      }

      const existingItem = await db.query.cartItems.findFirst({
        where: and(eq(cartItems.userId, userId), eq(cartItems.productId, productId)),
      });

      if (existingItem) {
        await db.update(cartItems)
          .set({
            quantity: sql`${cartItems.quantity} + ${quantity}`,
          })
          .where(eq(cartItems.id, existingItem.id));
      } else {
        await db.insert(cartItems).values({
          userId,
          productId,
          quantity: quantity.toString(),
        });
      }
      */

      // Return updated cart
      return await getCartData(userId)
    }),

  updateCartItem: protectedProcedure
    .input(z.object({
      itemId: z.number().int().positive(),
      quantity: z.number().int().min(0),
    }))
    .mutation(async ({ input, ctx }): Promise<UserCartResponse> => {
      const userId = ctx.user.userId;
      const { itemId, quantity } = input;

      if (!quantity || quantity <= 0) {
        throw new ApiError("Positive quantity required", 400);
      }

      const updated = await updateUserCartItemQuantityInDb(userId, itemId, quantity)

      /*
      // Old implementation - direct DB queries:
      const [updatedItem] = await db.update(cartItems)
        .set({ quantity: quantity.toString() })
        .where(and(
          eq(cartItems.id, itemId),
          eq(cartItems.userId, userId)
        ))
        .returning();

      if (!updatedItem) {
        throw new ApiError("Cart item not found", 404);
      }
      */

      if (!updated) {
        throw new ApiError('Cart item not found', 404)
      }

      // Return updated cart
      return await getCartData(userId)
    }),

  removeFromCart: protectedProcedure
    .input(z.object({
      itemId: z.number().int().positive(),
    }))
    .mutation(async ({ input, ctx }): Promise<UserCartResponse> => {
      const userId = ctx.user.userId;
      const { itemId } = input;

      const deleted = await deleteUserCartItemInDb(userId, itemId)

      /*
      // Old implementation - direct DB queries:
      const [deletedItem] = await db.delete(cartItems)
        .where(and(
          eq(cartItems.id, itemId),
          eq(cartItems.userId, userId)
        ))
        .returning();

      if (!deletedItem) {
        throw new ApiError("Cart item not found", 404);
      }
      */

      if (!deleted) {
        throw new ApiError('Cart item not found', 404)
      }

      // Return updated cart
      return await getCartData(userId)
    }),

  clearCart: protectedProcedure
    .mutation(async ({ ctx }): Promise<UserCartResponse> => {
      const userId = ctx.user.userId;

      await clearUserCartInDb(userId)

      /*
      // Old implementation - direct DB query:
      await db.delete(cartItems).where(eq(cartItems.userId, userId));
      */

      return {
        items: [],
        totalItems: 0,
        totalAmount: 0,
        message: "Cart cleared successfully",
      }
    }),

  // Original DB-based getCartSlots (commented out)
  // getCartSlots: publicProcedure
  //   .input(z.object({
  //     productIds: z.array(z.number().int().positive())
  //   }))
  //   .query(async ({ input }) => {
  //     const { productIds } = input;
  //
  //     if (productIds.length === 0) {
  //       return {};
  //     }
  //
  //     // Get slots for these products where freeze time is after current time
  //     const slotsData = await db
  //       .select({
  //         productId: productSlots.productId,
  //         slotId: deliverySlotInfo.id,
  //         deliveryTime: deliverySlotInfo.deliveryTime,
  //         freezeTime: deliverySlotInfo.freezeTime,
  //         isActive: deliverySlotInfo.isActive,
  //       })
  //       .from(productSlots)
  //       .innerJoin(deliverySlotInfo, eq(productSlots.slotId, deliverySlotInfo.id))
  //       .where(and(
  //         inArray(productSlots.productId, productIds),
  //         gt(deliverySlotInfo.freezeTime, sql`NOW()`),
  //         eq(deliverySlotInfo.isActive, true)
  //       ));
  //
  //     // Group by productId
  //     const result: Record<number, any[]> = {};
  //     slotsData.forEach(slot => {
  //       if (!result[slot.productId]) {
  //         result[slot.productId] = [];
  //       }
  //       result[slot.productId].push({
  //         id: slot.slotId,
  //         deliveryTime: slot.deliveryTime,
  //         freezeTime: slot.freezeTime,
  //       });
  //     });
  //
  //     return result;
  //   }),

  // Cache-based getCartSlots
  getCartSlots: publicProcedure
    .input(z.object({
      productIds: z.array(z.number().int().positive())
    }))
    .query(async ({ input }) => {
      const { productIds } = input;

      if (productIds.length === 0) {
        return {};
      }

      return await getMultipleProductsSlots(productIds);
    }),
});
