const { Cart, CartItem, Product, ProductImage, ProductVariant, Coupon } = require('../models');
const appConfig = require('../config/app');

class CartService {
  async getCart(userId, sessionId) {
    const where = userId ? { userId } : { sessionId };
    let cart = await Cart.findOne({
      where,
      include: [{
        model: CartItem,
        as: 'items',
        include: [
          { model: Product },
          { model: ProductVariant },
        ],
      }],
    });

    if (!cart) {
      cart = await Cart.create({ userId, sessionId });
      cart = await Cart.findByPk(cart.id, {
        include: [{
          model: CartItem,
          as: 'items',
          include: [
            { model: Product },
            { model: ProductVariant },
          ],
        }],
      });
    }

    return cart;
  }

  async addItem(userId, sessionId, productId, quantity = 1, variantId = null) {
    const cart = await this.getOrCreateCart(userId, sessionId);
    const product = await Product.findByPk(productId);
    if (!product || !product.isActive) {
      throw new Error('Product not found or unavailable.');
    }

    let unitPrice = parseFloat(product.price);
    if (variantId) {
      const variant = await ProductVariant.findByPk(variantId);
      if (variant) {
        unitPrice += parseFloat(variant.priceModifier);
      }
    }

    const existingItem = await CartItem.findOne({
      where: { cartId: cart.id, productId, variantId: variantId || null },
    });

    if (existingItem) {
      existingItem.quantity += quantity;
      await existingItem.save();
      return existingItem;
    }

    return CartItem.create({
      cartId: cart.id,
      productId,
      variantId,
      quantity,
      unitPrice,
    });
  }

  async updateItemQuantity(cartItemId, quantity) {
    const item = await CartItem.findByPk(cartItemId);
    if (!item) throw new Error('Cart item not found.');
    if (quantity <= 0) {
      await item.destroy();
      return null;
    }
    item.quantity = quantity;
    await item.save();
    return item;
  }

  async removeItem(cartItemId) {
    const item = await CartItem.findByPk(cartItemId);
    if (item) await item.destroy();
    return true;
  }

  async getOrCreateCart(userId, sessionId) {
    const where = userId ? { userId } : { sessionId };
    let cart = await Cart.findOne({ where });
    if (!cart) {
      cart = await Cart.create({ userId, sessionId });
    }
    return cart;
  }

  async getCartWithItems(cartId) {
    return Cart.findByPk(cartId, {
      include: [{
        model: CartItem,
        as: 'items',
        include: [
          {
            model: Product,
            include: [{
              model: ProductImage,
              as: 'images',
              where: { isPrimary: true },
              required: false,
            }],
          },
          { model: ProductVariant },
        ],
      }],
    });
  }

  calculateTotals(items, coupon = null) {
    let subtotal = 0;
    for (const item of items) {
      subtotal += parseFloat(item.unitPrice) * item.quantity;
    }

    let discountAmount = 0;
    if (coupon) {
      discountAmount = this.calculateDiscount(subtotal, coupon);
    }

    const taxAmount = parseFloat(((subtotal - discountAmount) * (appConfig.taxRate / 100)).toFixed(2));
    const shippingAmount = subtotal >= appConfig.shippingFreeMinAmount ? 0 : appConfig.shippingFlatRate;
    const total = parseFloat((subtotal - discountAmount + taxAmount + shippingAmount).toFixed(2));

    return { subtotal, discountAmount, taxAmount, shippingAmount, total };
  }

  calculateDiscount(subtotal, coupon) {
    if (!coupon || !coupon.isActive) return 0;
    const now = new Date();
    if ((coupon.startsAt && new Date(coupon.startsAt) > now) ||
        (coupon.expiresAt && new Date(coupon.expiresAt) < now)) {
      return 0;
    }
    if (coupon.maxUses && coupon.usedCount >= coupon.maxUses) return 0;
    if (coupon.minOrderAmount && subtotal < parseFloat(coupon.minOrderAmount)) return 0;

    let discount = 0;
    if (coupon.type === 'fixed') {
      discount = parseFloat(coupon.value);
    } else if (coupon.type === 'percentage') {
      discount = subtotal * (parseFloat(coupon.value) / 100);
      if (coupon.maxDiscountAmount && discount > parseFloat(coupon.maxDiscountAmount)) {
        discount = parseFloat(coupon.maxDiscountAmount);
      }
    }
    return Math.min(discount, subtotal);
  }

  async clearCart(cartId) {
    await CartItem.destroy({ where: { cartId } });
  }

  async applyCoupon(userId, sessionId, code) {
    const cart = await this.getOrCreateCart(userId, sessionId);
    const coupon = await Coupon.findOne({ where: { code, isActive: true } });
    if (!coupon) throw new Error('Invalid coupon code.');
    cart.couponId = coupon.id;
    await cart.save();
    return coupon;
  }

  async removeCoupon(userId, sessionId) {
    const cart = await this.getOrCreateCart(userId, sessionId);
    cart.couponId = null;
    cart.discountAmount = 0;
    await cart.save();
  }
}

module.exports = new CartService();
