import { router, protectedProcedure } from '@/src/trpc/trpc-index'
import { z } from 'zod'
import { ApiError } from '@/src/lib/api-error'
import {
  getUserActiveCouponsWithRelations as getUserActiveCouponsWithRelationsInDb,
  getUserAllCouponsWithRelations as getUserAllCouponsWithRelationsInDb,
  getUserReservedCouponByCode as getUserReservedCouponByCodeInDb,
  redeemUserReservedCoupon as redeemUserReservedCouponInDb,
} from '@/src/dbService'
import type {
  UserCouponDisplay,
  UserEligibleCouponsResponse,
  UserMyCouponsResponse,
  UserRedeemCouponResponse,
} from '@packages/shared'

const generateCouponDescription = (coupon: { discountPercent?: string | null; flatDiscount?: string | null; minOrder?: string | null; maxValue?: string | null }): string => {
  let desc = '';

  if (coupon.discountPercent) {
    desc += `${coupon.discountPercent}% off`;
  } else if (coupon.flatDiscount) {
    desc += `₹${coupon.flatDiscount} off`;
  }

  if (coupon.minOrder) {
    desc += ` on orders above ₹${coupon.minOrder}`;
  }

  if (coupon.maxValue) {
    desc += ` (max discount ₹${coupon.maxValue})`;
  }

  return desc;
};

export const userCouponRouter = router({
  getEligible: protectedProcedure
    .query(async ({ ctx }): Promise<UserEligibleCouponsResponse> => {
      try {

      const userId = ctx.user.userId;
      
      const allCoupons = await getUserActiveCouponsWithRelationsInDb(userId)

      /*
      // Old implementation - direct DB queries:
      const allCoupons = await db.query.coupons.findMany({
        where: and(
          eq(coupons.isInvalidated, false),
           or(
             isNull(coupons.validTill),
             gt(coupons.validTill, new Date())
           )
         ),
         with: {
           usages: {
             where: eq(couponUsage.userId, userId)
           },
           applicableUsers: {
             with: {
               user: true
             }
           },
           applicableProducts: {
             with: {
               product: true
             }
           },
         }
       });
      */

      // Filter to only coupons applicable to current user
      const applicableCoupons = allCoupons.filter(coupon => {
        if(!coupon.isUserBased) return true;
        const applicableUsers = coupon.applicableUsers || [];
        return applicableUsers.some(au => au.userId === userId);
      });

       return { success: true, data: applicableCoupons };
    }
    catch(e) {
      console.log(e)
      throw new ApiError("Unable to get coupons")
    }
    }),

  getProductCoupons: protectedProcedure
    .input(z.object({ productId: z.number().int().positive() }))
    .query(async ({ input, ctx }): Promise<UserEligibleCouponsResponse> => {
      const userId = ctx.user.userId;
      const { productId } = input;

      // Get all active, non-expired coupons
      const allCoupons = await getUserActiveCouponsWithRelationsInDb(userId)

      /*
      // Old implementation - direct DB queries:
      const allCoupons = await db.query.coupons.findMany({
        where: and(
          eq(coupons.isInvalidated, false),
          or(
            isNull(coupons.validTill),
            gt(coupons.validTill, new Date())
          )
        ),
         with: {
           usages: {
             where: eq(couponUsage.userId, userId)
           },
           applicableUsers: {
             with: {
               user: true
             }
           },
           applicableProducts: {
             with: {
               product: true
             }
           },
         }
       });
      */

      // Filter to only coupons applicable to current user and product
      const applicableCoupons = allCoupons.filter(coupon => {
        const applicableUsers = coupon.applicableUsers || [];
        const userApplicable = !coupon.isUserBased || applicableUsers.some(au => au.userId === userId);

        const applicableProducts = coupon.applicableProducts || [];
        const productApplicable = applicableProducts.length === 0 || applicableProducts.some(ap => ap.productId === productId);

        return userApplicable && productApplicable;
      });

      return { success: true, data: applicableCoupons };
    }),

  getMyCoupons: protectedProcedure
    .query(async ({ ctx }): Promise<UserMyCouponsResponse> => {
      const userId = ctx.user.userId;

      const allCoupons = await getUserAllCouponsWithRelationsInDb(userId)

      /*
      // Old implementation - direct DB queries:
      const allCoupons = await db.query.coupons.findMany({
        with: {
          usages: {
            where: eq(couponUsage.userId, userId)
          },
          applicableUsers: {
            with: {
              user: true
            }
          }
        }
      });
      */

      // Filter coupons in JS: not invalidated, applicable to user, and not expired
      const applicableCoupons = allCoupons.filter(coupon => {
        const isNotInvalidated = !coupon.isInvalidated;
        const applicableUsers = coupon.applicableUsers || [];
        const isApplicable = coupon.isApplyForAll || applicableUsers.some(au => au.userId === userId);
        const isNotExpired = !coupon.validTill || new Date(coupon.validTill) > new Date();
        return isNotInvalidated && isApplicable && isNotExpired;
      });

      // Categorize coupons
      const personalCoupons: UserCouponDisplay[] = [];
      const generalCoupons: UserCouponDisplay[] = [];

      applicableCoupons.forEach(coupon => {
        const usageCount = coupon.usages.length;
        const isExpired = false; // Already filtered out expired coupons
        const isUsedUp = Boolean(coupon.maxLimitForUser && usageCount >= coupon.maxLimitForUser);

        const couponDisplay: UserCouponDisplay = {
          id: coupon.id,
          code: coupon.couponCode,
          discountType: coupon.discountPercent ? 'percentage' : 'flat',
          discountValue: parseFloat(coupon.discountPercent || coupon.flatDiscount || '0'),
          maxValue: coupon.maxValue ? parseFloat(coupon.maxValue) : undefined,
          minOrder: coupon.minOrder ? parseFloat(coupon.minOrder) : undefined,
          description: generateCouponDescription(coupon),
          validTill: coupon.validTill ? new Date(coupon.validTill) : undefined,
          usageCount,
          maxLimitForUser: coupon.maxLimitForUser ? parseInt(coupon.maxLimitForUser.toString()) : undefined,
          isExpired,
          isUsedUp,
        };

        if ((coupon.applicableUsers || []).some(au => au.userId === userId) && !coupon.isApplyForAll) {
          // Personal coupon
          personalCoupons.push(couponDisplay);
        } else if (coupon.isApplyForAll) {
          // General coupon
          generalCoupons.push(couponDisplay);
        }
      });

      return {
        success: true,
        data: {
          personal: personalCoupons,
          general: generalCoupons,
        }
      };
    }),

  redeemReservedCoupon: protectedProcedure
    .input(z.object({ secretCode: z.string() }))
    .mutation(async ({ input, ctx }): Promise<UserRedeemCouponResponse> => {
      const userId = ctx.user.userId;
      const { secretCode } = input;

      const reservedCoupon = await getUserReservedCouponByCodeInDb(secretCode)

      /*
      // Old implementation - direct DB queries:
      const reservedCoupon = await db.query.reservedCoupons.findFirst({
        where: and(
          eq(reservedCoupons.secretCode, secretCode.toUpperCase()),
          eq(reservedCoupons.isRedeemed, false)
        ),
      });
      */

      if (!reservedCoupon) {
        throw new ApiError("Invalid or already redeemed coupon code", 400);
      }

      // Check if already redeemed by this user (in case of multiple attempts)
      if (reservedCoupon.redeemedBy === userId) {
        throw new ApiError("You have already redeemed this coupon", 400);
      }

      const couponResult = await redeemUserReservedCouponInDb(userId, reservedCoupon)

      /*
      // Old implementation - direct DB queries:
      const couponResult = await db.transaction(async (tx) => {
        const couponInsert = await tx.insert(coupons).values({
          couponCode: reservedCoupon.couponCode,
          isUserBased: true,
          discountPercent: reservedCoupon.discountPercent,
          flatDiscount: reservedCoupon.flatDiscount,
          minOrder: reservedCoupon.minOrder,
          productIds: reservedCoupon.productIds,
          maxValue: reservedCoupon.maxValue,
          isApplyForAll: false,
          validTill: reservedCoupon.validTill,
          maxLimitForUser: reservedCoupon.maxLimitForUser,
          exclusiveApply: reservedCoupon.exclusiveApply,
          createdBy: reservedCoupon.createdBy,
        }).returning();

        const coupon = couponInsert[0];

        await tx.insert(couponApplicableUsers).values({
          couponId: coupon.id,
          userId,
        });

        await tx.update(reservedCoupons).set({
          isRedeemed: true,
          redeemedBy: userId,
          redeemedAt: new Date(),
        }).where(eq(reservedCoupons.id, reservedCoupon.id));

        return coupon;
      });
      */

      return { success: true, coupon: couponResult };
    }),
});
