import { Inject, Injectable, forwardRef } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import * as moment from 'jalali-moment';

import { ErrorService } from '../../error/error.service';
import { CarService } from '../car/car.service';
import { SiteInfoService } from '../siteInfo/site-info.service';
import { GhabzinoService } from '../../thirdParty/ghabzino/ghabzino.service';
import {
  FreewayTollEntity,
  FreewayTollStatus,
} from './entities/freeway-toll.entity';
import { CreateFreewayTollRequestDto } from './dto/create-freeway-toll-request.dto';
import { PayFreewayTollInquiryDto } from './dto/pay-freeway-toll-inquiry.dto';
import { UserEntity } from '../user/entities/user.entity';
import { FreewayTollBill } from '../../thirdParty/ghabzino/ghabzino.interface';
import { FreewayTollBillEntity } from './entities/freeway-toll-bill.entity';
import { PaymentService } from '../payment/payment.service';
import { PaymentType } from '../payment/entities/payment.entity';
import { UserService } from '../user/user.service';
import {
  applyFiltersToBuilder,
  applySortingToBuilder,
  paginationResult,
} from '../../utils';

@Injectable()
export class FreewayTollService {
  constructor(
    @InjectRepository(FreewayTollEntity)
    private freewayTollRepository: Repository<FreewayTollEntity>,

    @InjectRepository(FreewayTollBillEntity)
    private freewayTollBillRepository: Repository<FreewayTollBillEntity>,

    private error: ErrorService,
    private carService: CarService,
    private ghabzinoService: GhabzinoService,
    private userService: UserService,
    private siteInfoService: SiteInfoService,

    @Inject(forwardRef(() => PaymentService))
    private readonly paymentService: PaymentService,
  ) {}

  /**
   * -------------------------------------------------------
   * POST /freeway-tolls/request/me
   */
  async addRequest(dto: CreateFreewayTollRequestDto, user: UserEntity) {
    const { freewayTollInquiryAmount } = await this.siteInfoService.getInfo([
      'freewayTollInquiryAmount',
    ]);

    const foundCar = await this.carService.getCarRecordById(dto.carId, user.id);

    if (!foundCar) {
      this.error.unprocessableEntity(['درخواست شما نامعتبر است']);
    }

    const { identifiers } = await this.freewayTollRepository
      .createQueryBuilder()
      .insert()
      .values({
        ...dto,
        userId: user.id,
        inquiryAmount: freewayTollInquiryAmount,
        status: freewayTollInquiryAmount
          ? FreewayTollStatus.pending
          : FreewayTollStatus.paid_inquiry,
        plateNumber: foundCar.number,
        createdAt: new Date(),
        updatedAt: new Date(),
      })
      .execute();

    return { id: identifiers[0].id };
  }

  /**
   * -------------------------------------------------------
   * POST /freeway-tolls/pay-inquiry/me
   */
  async payInquiry(dto: PayFreewayTollInquiryDto, user: UserEntity) {
    const freewayToll = await this.freewayTollRepository.findOne({
      where: {
        id: dto.freewayTollId,
        userId: user.id,
        status: FreewayTollStatus.pending,
      },
      relations: ['user'],
    });

    if (!freewayToll) {
      this.error.unprocessableEntity(['درخواست شما نامعتبر است']);
    }

    let bankPayAmount = freewayToll.inquiryAmount;
    let usedWalletAmount = null;

    // Using the wallet
    if (dto.useWallet) {
      const totalWallet = user.realWallet + user.virtualWallet;

      // Enough money in wallet
      if (totalWallet >= freewayToll.inquiryAmount) {
        freewayToll.status = FreewayTollStatus.paid_inquiry;
        await freewayToll.save();

        await this.userService.reduceBothWallets(
          user.id,
          freewayToll.inquiryAmount,
        );

        return { bankUrl: null, paid: true };
      }

      // After payment, totalWallet will be reduced from the wallet
      bankPayAmount -= totalWallet;
      usedWalletAmount = totalWallet;
    }

    // Going to the bank
    const { paymentId, bankUrl } = await this.paymentService.createPayLink(
      bankPayAmount,
      usedWalletAmount,
      PaymentType.freeway_inquiry,
      dto.payGateway,
      freewayToll.id,
      user,
    );

    freewayToll.paymentInquiryId = paymentId;
    await freewayToll.save();

    return { bankUrl, paid: false };
  }

  /**
   * -------------------------------------------------------
   * GET /freeway-tolls/1/do-inquiry/me
   * call from front or payment service
   */
  async doInquiry(
    paymentId: number = null,
    freewayTollId: number = null,
    user: UserEntity = null,
  ) {
    let freeway: FreewayTollEntity;

    // Request from front-end
    if (freewayTollId) {
      freeway = await this.freewayTollRepository.findOne({
        where: { id: freewayTollId, userId: user.id },
        relations: ['user', 'car', 'freewayTollBills'],
      });

      if (
        freeway?.inquiryAmount &&
        freeway?.status === FreewayTollStatus.pending
      ) {
        this.error.unprocessableEntity(['درخواست شما نامعتبر است']);
      }
    }
    // Request after payment by paymentService
    else {
      freeway = await this.freewayTollRepository.findOne({
        where: { paymentInquiryId: paymentId },
        relations: ['user', 'car', 'freewayTollBills'],
      });
    }

    // Already inquired the freeway
    if (
      [FreewayTollStatus.inquired, FreewayTollStatus.paid_freeway_toll].indexOf(
        freeway.status,
      ) !== -1
    ) {
      delete freeway.user;
      delete freeway.car;
      return freeway;
    }

    freeway.status = FreewayTollStatus.paid_inquiry;
    await freeway.save();

    // Inquiring the freeway
    const result = await this.ghabzinoService.freewayTollBillInquiry(
      freeway.car.number,
    );

    if (!result.hasBill) {
      freeway.status = FreewayTollStatus.inquired;
      freeway.freewayTollAmount = 0;
      await freeway.save();
      this.error.unprocessableEntity(['عوارضی جهت پرداخت یافت نشد']);
    }

    const freewayTollAmount = result.bills.reduce(
      (acc, bill: FreewayTollBill) => +bill.Amount + acc,
      0,
    );

    // update freeway
    freeway.freewayTollAmount = freewayTollAmount / 10;
    freeway.status = FreewayTollStatus.inquired;
    freeway.updatedAt = new Date();
    await freeway.save();

    // insert freeway bills
    await this.freewayTollBillRepository
      .createQueryBuilder()
      .insert()
      .values(
        result.bills.map((bill) => ({
          freewayTollId: freeway.id,
          amount: +bill.Amount / 10,
          fee: +bill.Fee / 10, //////////////////////////
          freeway: bill.Freeway,
          totalAmount: +bill.TotalAmount / 10,
          paymentStatus: bill.PaymentStatus,
          paymentStatusShowName: bill.PaymentStatusShowName,
          issueDateTime: bill.IssueDateTime,
          // issueDateTime: moment(
          //   bill.IssueDateTime,
          //   'DD/MM/YYYY HH:mm:ss',
          // ).toDate(),
          traverseDateTime: bill.TraverseDateTime,
          uniqueId: bill.UniqueID,
        })),
      )
      .execute();

    const finalFreeway = await this.freewayTollRepository.findOne({
      where: { id: freeway.id },
      relations: ['freewayTollBills'],
    });

    return finalFreeway;
  }

  /**
   * -------------------------------------------------------
   * POST /freeway-tolls/pay-bills/me
   */
  async payBills(freewayTollId: number, user: UserEntity) {
    const freewayToll = await this.freewayTollRepository.findOne({
      where: {
        id: freewayTollId,
        userId: user.id,
        status: FreewayTollStatus.inquired,
      },
      relations: ['freewayTollBills'],
    });

    const { bankUrl, paymentKey } =
      await this.ghabzinoService.freewayTollBillPay(
        freewayToll.freewayTollBills.map((bill) => bill.uniqueId),
        user.mobile,
      );

    freewayToll.paymentKey = paymentKey;
    await freewayToll.save();

    return { bankUrl };
  }

  /**
   * -------------------------------------------------------
   * POST /freeway-tolls/1/pay-result-bills/me
   */
  async payResultBills(freewayTollId: number, user: UserEntity) {
    const freewayToll = await this.freewayTollRepository.findOne({
      where: {
        id: freewayTollId,
        userId: user.id,
        status: FreewayTollStatus.inquired,
      },
      relations: ['freewayTollBills'],
    });

    const result = await this.ghabzinoService.freewayTollBillPayResult(
      freewayToll.paymentKey,
    );

    if (result.PaymentStatusName !== 'Paid') {
      this.error.unprocessableEntity(['پرداخت انجام نشد!']);
    }

    freewayToll.status = FreewayTollStatus.paid_freeway_toll;
    await freewayToll.save();

    for (const bill of freewayToll.freewayTollBills) {
      bill.paymentStatus = 'Paid';
      bill.paymentStatusShowName = 'پرداخت شده';
      await bill.save();
    }

    return freewayToll;
  }

  /**
   * -------------------------------------------------------
   * GET /freeway-tolls
   */
  async getAll(
    page = 1,
    limit = 20,
    filters = null,
    sorts = null,
    userId = null,
  ) {
    let builder = this.freewayTollRepository
      .createQueryBuilder('freewayToll')
      .innerJoinAndSelect('freewayToll.car', 'car')
      .innerJoin('freewayToll.user', 'user')
      .addSelect([
        'user.id',
        'user.name',
        'user.surName',
        'user.mobile',
        'user.licenseNumber',
      ])
      .take(limit)
      .skip((page - 1) * limit);

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

    builder = applyFiltersToBuilder(builder, filters);

    if (userId) {
      builder.andWhere({ userId });
    }

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

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

  /**
   * -------------------------------------------------------
   * GET /freeway-tolls/1
   */
  async getById(id: number, userId = null) {
    const builder = this.freewayTollRepository
      .createQueryBuilder('freewayToll')
      .leftJoinAndSelect('freewayToll.freewayTollBills', 'freewayTollBills')
      .innerJoinAndSelect('freewayToll.car', 'car')
      .leftJoinAndSelect('freewayToll.payment', 'payment')
      .innerJoin('freewayToll.user', 'user')
      .addSelect([
        'user.id',
        'user.name',
        'user.surName',
        'user.mobile',
        'user.licenseNumber',
      ])
      .andWhere({ id });

    if (userId) {
      builder.andWhere({ userId });
    }

    return await builder.getOne();
  }
}
