import { Injectable, BadRequestException } from '@nestjs/common';
import { DataSource, EntityManager } from 'typeorm';
import { Coupon } from './entities/coupon.entity';
import { CouponRedemption } from './entities/coupon-redemption.entity';

@Injectable()
export class CouponService {
  constructor(private dataSource: DataSource) {}

  async validateCoupon(
    code: string,
    userId: number,
    purchaseAmount: number,
    manager?: EntityManager,
  ) {
    const execute = async (em: EntityManager) => {
      const [coupon] = await em.query('SELECT * FROM coupons WHERE code = ?', [
        code,
      ]);

      // 1. Basic Checks
      if (!coupon) throw new BadRequestException('Invalid coupon code');
      if (!coupon.is_active)
        throw new BadRequestException('Coupon is inactive');

      const now = new Date();
      if (
        now < new Date(coupon.valid_from) ||
        now > new Date(coupon.valid_to)
      ) {
        throw new BadRequestException('Coupon is expired');
      }

      // 2. Global Limit
      if (coupon.usage_limit && coupon.usage_limit > 0) {
        const [globalUsage] = await em.query(
          'SELECT COUNT(*) as c FROM coupon_redemptions WHERE coupon_id = ?',
          [coupon.id],
        );
        if (globalUsage.c >= coupon.usage_limit)
          throw new BadRequestException('Coupon usage limit reached');
      }

      // 3. User Limit
      const [userUsage] = await em.query(
        'SELECT COUNT(*) as c FROM coupon_redemptions WHERE coupon_id = ? AND user_id = ?',
        [coupon.id, userId],
      );
      if (userUsage.c >= coupon.per_user_limit)
        throw new BadRequestException('You have already used this coupon');

      // 4. Min Purchase
      if (purchaseAmount < Number(coupon.min_purchase_amount)) {
        throw new BadRequestException(
          `Minimum purchase amount of ${coupon.min_purchase_amount} required`,
        );
      }

      return coupon;
    };

    if (manager) return await execute(manager);
    return await this.dataSource.transaction(execute);
  }

  calculateDiscount(coupon: Coupon, amount: number): number {
    let discount = 0;
    if (coupon.discount_type === 'percentage') {
      discount = (amount * coupon.discount_value) / 100;
      if (coupon.max_discount_amount) {
        discount = Math.min(discount, Number(coupon.max_discount_amount));
      }
    } else {
      discount = Number(coupon.discount_value);
    }
    return Math.min(discount, amount); // Cannot exceed amount
  }

  // --- CRUD ---

  async findAll() {
    return await this.dataSource.getRepository(Coupon).find({
      order: { created_at: 'DESC' },
    });
  }

  async create(data: Partial<Coupon>) {
    const coupon = this.dataSource.getRepository(Coupon).create(data);
    return await this.dataSource.getRepository(Coupon).save(coupon);
  }

  async update(id: number, data: Partial<Coupon>) {
    await this.dataSource.getRepository(Coupon).update(id, data);
    return await this.dataSource
      .getRepository(Coupon)
      .findOne({ where: { id } });
  }

  async delete(id: number) {
    return await this.dataSource.getRepository(Coupon).delete(id);
  }
}
