import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import {
  applyFiltersToBuilder,
  applySortingToBuilder,
  paginationResult,
} from '../../utils';
import { Repository } from 'typeorm';
import {
  PromotionPlanEntity,
  PromotionPlanTypes,
} from './entities/promotion-plan.entity';
import { CreatePromotionPlanDto } from './dto/create-promotion-plan.dto';
import { UpdatePromotionPlanDto } from './dto/update-promotion-plan.dto';
import { ErrorService } from '../../error/error.service';
import { UserEntity } from '../user/entities/user.entity';
import * as moment from 'jalali-moment';
import { DiscountService } from '../discount/discount.service';
import { SmsService } from '../../sms/sms.service';
import { CreateDiscountDto } from '../discount/dto/create-discount.dto';

@Injectable()
export class PromotionPlanService {
  constructor(
    @InjectRepository(PromotionPlanEntity)
    private promotionPlanRepository: Repository<PromotionPlanEntity>,

    private discountService: DiscountService,
    private error: ErrorService,
    private sms: SmsService,
  ) {}

  /**
   * -------------------------------------------------------
   */
  async getAll(page = 1, limit = 20, filters = null, sorts = null) {
    let builder = this.promotionPlanRepository
      .createQueryBuilder('promotionPlan')
      .andWhere({ deleted: false })
      .leftJoinAndSelect('promotionPlan.customer', 'customer')
      .take(limit)
      .skip((page - 1) * limit);

    if (sorts) {
      builder = applySortingToBuilder(builder, sorts);
    } else {
      builder.orderBy('promotionPlan.createdAt', 'DESC');
    }

    builder = applyFiltersToBuilder(builder, filters);

    const [items, totalItems] = await builder.getManyAndCount();

    return {
      items,
      pagination: paginationResult(page, limit, totalItems),
    };
  }

  /**
   * -------------------------------------------------------
   */
  async getById(id: number) {
    return await this.promotionPlanRepository
      .createQueryBuilder('promotionPlan')
      .where({ id, deleted: false })
      .getOne();
  }

  /**
   * -------------------------------------------------------
   */
  async create(dto: CreatePromotionPlanDto) {
    const newPromotionPlan = new PromotionPlanEntity();
    for (const key in dto) {
      newPromotionPlan[key] = dto[key];
    }

    if (dto.discountPercentage !== undefined) {
      newPromotionPlan.usePercentage = !!dto.discountPercentage;
    }

    newPromotionPlan.createdAt = new Date();
    newPromotionPlan.updatedAt = new Date();

    await this.promotionPlanRepository
      .createQueryBuilder()
      .insert()
      .values(newPromotionPlan)
      .execute();

    return true;
  }

  /**
   * -------------------------------------------------------
   */
  async updateById(dto: UpdatePromotionPlanDto) {
    const promotionPlan = await this.promotionPlanRepository.findOne(dto.id);
    for (const key in dto) {
      promotionPlan[key] = dto[key];
    }

    if (dto.discountPercentage !== undefined) {
      promotionPlan.usePercentage = !!dto.discountPercentage;

      if (promotionPlan.usePercentage) {
        promotionPlan.discountAmount = null;
      } else {
        promotionPlan.discountPercentage = null;
      }
    }

    promotionPlan.updatedAt = new Date();

    this.promotionPlanRepository
      .createQueryBuilder()
      .update()
      .set(promotionPlan)
      .where({ id: dto.id })
      .execute();
  }

  /**
   * -------------------------------------------------------
   */
  async deletePromotionPlan(id: number) {
    return await this.promotionPlanRepository
      .createQueryBuilder()
      .update()
      .set({ deleted: true, updatedAt: new Date() })
      .where({ id })
      .execute();
  }

  /**
   * -------------------------------------------------------
   */
  async runPromotion(type: PromotionPlanTypes, user: UserEntity) {
    const builder = this.promotionPlanRepository
      .createQueryBuilder()
      .where({ deleted: false, type })
      .andWhere(
        '((startDate IS NULL AND endDate IS NULL) OR (startDate <= :currentDate AND endDate >= :currentDate))',
        { currentDate: moment().format('YYYY-MM-DD') },
      )
      .orderBy('customerId', 'DESC')
      .addOrderBy('startDate', 'DESC');

    if (user.customerId) {
      builder.andWhere('(customerId IS NULL OR customerId = :customerId)', {
        customerId: user.customerId,
      });
    } else {
      builder.andWhere('customerId IS NULL');
    }

    const promotions = await builder.getMany();

    if (promotions?.length === 0) {
      this.error.unprocessableEntity(['طرح تشویقی فعالی یافت نشد']);
    }

    const promotion = promotions[0];

    switch (promotion.type) {
      case PromotionPlanTypes.signupDiscount:
        await this._runSignupDiscount(promotion, user);
        break;
    }

    return promotion;
  }

  /**
   * -------------------------------------------------------
   */
  private async _runSignupDiscount(
    promotion: PromotionPlanEntity,
    user: UserEntity,
  ) {
    const dto: CreateDiscountDto = {
      countCouponCode: 1,
      customerId: promotion.customerId,
      discountAmount: promotion.discountAmount,
      discountPercentage: promotion.discountPercentage,
      name: promotion.name,
      section: promotion.section,
      promotionPlanId: promotion.id,
      userId: user.id,
    };

    if (promotion.validityDays) {
      dto.startDate = moment().startOf('day').toDate();
      dto.endDate = moment()
        .add(promotion.validityDays, 'days')
        .endOf('day')
        .toDate();
    }

    const discounts = await this.discountService.create(dto);

    // Sending the discount via sms
    try {
      await this.sms.sendSignupDiscount(
        user.mobile,
        `${user.name || ''} ${user.surName || ''}`.trim() || '-',
        discounts?.[0]?.couponCode || '',
        promotion.validityDays
          ? moment()
              .add(promotion.validityDays, 'days')
              .locale('fa')
              .format('D MMMM')
          : null,
      );
    } catch (e) {}
  }
}
