import { router, publicProcedure, protectedProcedure } from '../trpc-index';
import { z } from 'zod';
import { db } from '../../db/db_index';
import { productInfo, units, productSlots, deliverySlotInfo, specialDeals, storeInfo, productTagInfo, productTags, productReviews, users } from '../../db/schema';
import { generateSignedUrlsFromS3Urls, generateSignedUrlFromS3Url, generateUploadUrl, claimUploadUrl, extractKeyFromPresignedUrl } from '../../lib/s3-client';
import { ApiError } from '../../lib/api-error';
import { eq, and, gt, sql, inArray, desc } from 'drizzle-orm';
import { getProductById as getProductByIdFromCache, getAllProducts as getAllProductsFromCache } from '../../stores/product-store';
import dayjs from 'dayjs';

// Uniform Product Type
interface Product {
  id: number;
  name: string;
  shortDescription: string | null;
  longDescription: string | null;
  price: string;
  marketPrice: string | null;
  unitNotation: string;
  images: string[];
  isOutOfStock: boolean;
  store: { id: number; name: string; description: string | null } | null;
  incrementStep: number;
  productQuantity: number;
  isFlashAvailable: boolean;
  flashPrice: string | null;
  deliverySlots: Array<{ id: number; deliveryTime: Date; freezeTime: Date }>;
  specialDeals: Array<{ quantity: string; price: string; validTill: Date }>;
}

export const productRouter = router({
  getProductDetails: publicProcedure
    .input(z.object({
      id: z.string().regex(/^\d+$/, 'Invalid product ID'),
    }))
    .query(async ({ input }): Promise<Product> => {
      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
        const currentTime = new Date();
        const filteredSlots = cachedProduct.deliverySlots.filter(slot => 
          dayjs(slot.freezeTime).isAfter(currentTime)
        );
        
        return {
          ...cachedProduct,
          deliverySlots: filteredSlots
        };
      }

      // If not in cache, fetch from database (fallback)
      const productData = await db
        .select({
          id: productInfo.id,
          name: productInfo.name,
          shortDescription: productInfo.shortDescription,
          longDescription: productInfo.longDescription,
          price: productInfo.price,
          marketPrice: productInfo.marketPrice,
          images: productInfo.images,
          isOutOfStock: productInfo.isOutOfStock,
          storeId: productInfo.storeId,
          unitShortNotation: units.shortNotation,
          incrementStep: productInfo.incrementStep,
          productQuantity: productInfo.productQuantity,
          isFlashAvailable: productInfo.isFlashAvailable,
          flashPrice: productInfo.flashPrice,
        })
        .from(productInfo)
        .innerJoin(units, eq(productInfo.unitId, units.id))
        .where(eq(productInfo.id, productId))
        .limit(1);

      if (productData.length === 0) {
        throw new Error('Product not found');
      }

      const product = productData[0];

      // Fetch store info for this product
      const storeData = product.storeId ? await db.query.storeInfo.findFirst({
        where: eq(storeInfo.id, product.storeId),
        columns: { id: true, name: true, description: true },
      }) : null;

      // Fetch delivery slots for this product
      const deliverySlotsData = await db
        .select({
          id: deliverySlotInfo.id,
          deliveryTime: deliverySlotInfo.deliveryTime,
          freezeTime: deliverySlotInfo.freezeTime,
        })
        .from(productSlots)
        .innerJoin(deliverySlotInfo, eq(productSlots.slotId, deliverySlotInfo.id))
        .where(
          and(
            eq(productSlots.productId, productId),
            eq(deliverySlotInfo.isActive, true),
            gt(deliverySlotInfo.deliveryTime, sql`NOW()`),
            gt(deliverySlotInfo.freezeTime, sql`NOW()`)
          )
        )
        .orderBy(deliverySlotInfo.deliveryTime);

      // Fetch special deals for this product
      const specialDealsData = await db
        .select({
          quantity: specialDeals.quantity,
          price: specialDeals.price,
          validTill: specialDeals.validTill,
        })
        .from(specialDeals)
        .where(
          and(
            eq(specialDeals.productId, productId),
            gt(specialDeals.validTill, sql`NOW()`)
          )
        )
        .orderBy(specialDeals.quantity);

      // Generate signed URLs for images
      const signedImages = await generateSignedUrlsFromS3Urls((product.images as string[]) || []);

      const response: Product = {
        id: product.id,
        name: product.name,
        shortDescription: product.shortDescription,
        longDescription: product.longDescription,
        price: product.price.toString(),
        marketPrice: product.marketPrice?.toString() || null,
        unitNotation: product.unitShortNotation,
        images: signedImages,
        isOutOfStock: product.isOutOfStock,
        store: storeData ? {
          id: storeData.id,
          name: storeData.name,
          description: storeData.description,
        } : null,
        incrementStep: product.incrementStep,
        productQuantity: product.productQuantity,
        isFlashAvailable: product.isFlashAvailable,
        flashPrice: product.flashPrice?.toString() || null,
        deliverySlots: deliverySlotsData,
        specialDeals: specialDealsData.map(d => ({ quantity: d.quantity.toString(), price: d.price.toString(), validTill: d.validTill })),
      };

      return response;
     }),

  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 }) => {
      const { productId, limit, offset } = input;

      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);

      // Generate signed URLs for images
      const reviewsWithSignedUrls = await Promise.all(
        reviews.map(async (review) => ({
          ...review,
          signedImageUrls: await generateSignedUrlsFromS3Urls((review.imageUrls as string[]) || []),
        }))
      );

      // Check if more reviews exist
      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;

      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 }) => {
      const { productId, reviewBody, ratings, imageUrls, uploadUrls } = input;
      const userId = ctx.user.userId;

      // Optional: Check if product exists
      const product = await db.query.productInfo.findFirst({
        where: eq(productInfo.id, productId),
      });
      if (!product) {
        throw new ApiError('Product not found', 404);
      }

      // Insert review
      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<Product[]> => {
      // 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 = allCachedProducts.map(product => ({
        ...product,
        deliverySlots: [], // Empty for summary view
        specialDeals: [], // Empty for summary view
      }));

      return transformedProducts;
    }),

});