import { router, publicProcedure, protectedProcedure } from '@/src/trpc/trpc-index'
import { z } from 'zod'
import { claimUploadUrl, extractKeyFromPresignedUrl, scaffoldAssetUrl } from '@/src/lib/s3-client'
import { ApiError } from '@/src/lib/api-error'
import { getProductById as getProductByIdFromCache, getAllProducts as getAllProductsFromCache } from '@/src/stores/product-store'
import dayjs from 'dayjs'
import {
  getUserProductDetailById as getUserProductDetailByIdInDb,
  getUserProductReviews as getUserProductReviewsInDb,
  getUserProductByIdBasic as getUserProductByIdBasicInDb,
  createUserProductReview as createUserProductReviewInDb,
} from '@/src/dbService'
import type {
  UserProductDetail,
  UserProductDetailData,
  UserProductReviewsResponse,
  UserCreateReviewResponse,
  UserProductReviewWithSignedUrls,
} from '@packages/shared'

const signProductImages = (product: UserProductDetailData): UserProductDetail => ({
  ...product,
  images: scaffoldAssetUrl(product.images || []),
})

export const productRouter = router({
  getProductDetails: publicProcedure
    .input(z.object({
      id: z.string().regex(/^\d+$/, 'Invalid product ID'),
    }))
    .query(async ({ input }): Promise<UserProductDetail> => {
      const { id } = input;
      const productId = parseInt(id);

      if (isNaN(productId)) {
        throw new Error('Invalid product ID');
      }

      console.log('from the api to get product details')

// First, try to get the product from Redis cache
      const cachedProduct = await getProductByIdFromCache(productId);
      
      if (cachedProduct) {
        // Filter delivery slots to only include those with future freeze times and not at full capacity
        const currentTime = new Date();
        const filteredSlots = cachedProduct.deliverySlots.filter(slot => 
          dayjs(slot.freezeTime).isAfter(currentTime) && !slot.isCapacityFull
        );
        
        return {
          ...cachedProduct,
          deliverySlots: filteredSlots
        };
      }

      // If not in cache, fetch from database (fallback)
      const productData = await getUserProductDetailByIdInDb(productId)

      if (!productData) {
        throw new Error('Product not found')
      }

      return signProductImages(productData)
     }),

  getProductReviews: publicProcedure
    .input(z.object({
      productId: z.number().int().positive(),
      limit: z.number().int().min(1).max(50).optional().default(10),
      offset: z.number().int().min(0).optional().default(0),
    }))
    .query(async ({ input }): Promise<UserProductReviewsResponse> => {
      const { productId, limit, offset } = input;

      const { reviews, totalCount } = await getUserProductReviewsInDb(productId, limit, offset)

      /*
      // Old implementation - direct DB queries:
      const reviews = await db
        .select({
          id: productReviews.id,
          reviewBody: productReviews.reviewBody,
          ratings: productReviews.ratings,
          imageUrls: productReviews.imageUrls,
          reviewTime: productReviews.reviewTime,
          userName: users.name,
        })
        .from(productReviews)
        .innerJoin(users, eq(productReviews.userId, users.id))
        .where(eq(productReviews.productId, productId))
        .orderBy(desc(productReviews.reviewTime))
        .limit(limit)
        .offset(offset);

      const totalCountResult = await db
        .select({ count: sql`count(*)` })
        .from(productReviews)
        .where(eq(productReviews.productId, productId));

      const totalCount = Number(totalCountResult[0].count);
      const hasMore = offset + limit < totalCount;
      */

      const reviewsWithSignedUrls: UserProductReviewWithSignedUrls[] = reviews.map((review) => ({
        ...review,
        signedImageUrls: scaffoldAssetUrl(review.imageUrls || []),
      }))

      const hasMore = offset + limit < totalCount

      return { reviews: reviewsWithSignedUrls, hasMore }
    }),

  createReview: protectedProcedure
    .input(z.object({
      productId: z.number().int().positive(),
      reviewBody: z.string().min(1, 'Review body is required'),
      ratings: z.number().int().min(1).max(5),
      imageUrls: z.array(z.string()).optional().default([]),
      uploadUrls: z.array(z.string()).optional().default([]),
    }))
    .mutation(async ({ input, ctx }): Promise<UserCreateReviewResponse> => {
      const { productId, reviewBody, ratings, imageUrls, uploadUrls } = input;
      const userId = ctx.user.userId;

      const product = await getUserProductByIdBasicInDb(productId)
      if (!product) {
        throw new ApiError('Product not found', 404)
      }

      const imageKeys = uploadUrls.map(item => extractKeyFromPresignedUrl(item))
      const newReview = await createUserProductReviewInDb(userId, productId, reviewBody, ratings, imageKeys)

      /*
      // 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 [newReview] = await db.insert(productReviews).values({
        userId,
        productId,
        reviewBody,
        ratings,
        imageUrls: uploadUrls.map(item => extractKeyFromPresignedUrl(item)),
      }).returning();
      */

      // Claim upload URLs
      if (uploadUrls && uploadUrls.length > 0) {
        try {
          await Promise.all(uploadUrls.map(url => claimUploadUrl(url)));
        } catch (error) {
          console.error('Error claiming upload URLs:', error);
          // Don't fail the review creation
        }
      }

      return { success: true, review: newReview }
    }),

   
  getAllProductsSummary: publicProcedure
    .query(async (): Promise<UserProductDetail[]> => {
      // Get all products from cache
      const allCachedProducts = await getAllProductsFromCache();

      // Transform the cached products to match the expected summary format
      // (with empty deliverySlots and specialDeals arrays for summary view)
      const transformedProducts: UserProductDetail[] = allCachedProducts.map(product => ({
        ...product,
        images: product.images || [],
        deliverySlots: [],
        specialDeals: [],
      }))

      return transformedProducts
    }),

});
