import { Injectable } from '@nestjs/common';
import { ErrorService } from '../../error/error.service';
import { RayabimehService } from '../../thirdParty/rayabimeh/rayabimeh.service';
import { CarService } from '../car/car.service';
import { UserEntity } from '../user/entities/user.entity';
import { retryOperation, sleep, toEnglishDigit } from '../../utils';
import { InjectRepository } from '@nestjs/typeorm';
import {
  InsuranceInquiryClient,
  InsuranceThirdPartyInquiryEntity,
} from './entities/insurance-third-party-inquiry.entity';
import { MoreThan, Repository } from 'typeorm';
import * as moment from 'jalali-moment';

@Injectable()
export class InsuranceService {
  constructor(
    @InjectRepository(InsuranceThirdPartyInquiryEntity)
    private insuranceThirdPartyInquiryRepository: Repository<InsuranceThirdPartyInquiryEntity>,

    private error: ErrorService,
    private rayabimehService: RayabimehService,
    private carService: CarService,
  ) {}

  /**
   * -------------------------------------------------------
   * doing inquery each 1 hour once
   */
  async getThirdPartyInquiry(carId: number, user: UserEntity) {
    const foundCar = await this.carService.getCarRecordById(carId, user.id);

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

    if (!user.nationalCode) {
      this.error.unprocessableEntity(['ورود کد ملی اجباری است']);
    }

    const prevInquiry = await this.insuranceThirdPartyInquiryRepository.findOne(
      {
        where: {
          carId: foundCar.id,
          createdAt: MoreThan(moment().subtract(1, 'hour').toDate()),
        },
        order: { createdAt: 'DESC' },
      },
    );

    if (prevInquiry) {
      return prevInquiry;
    }

    const res = await retryOperation(
      () =>
        this.rayabimehService.thirdPartyInsuranceInquiry(
          toEnglishDigit(foundCar.number),
          toEnglishDigit(user.nationalCode),
        ),
      2,
      700,
    );

    if (res.success) {
      if (res.result === null) {
        return null;
      } else {
        const { identifiers } = await this.insuranceThirdPartyInquiryRepository
          .createQueryBuilder()
          .insert()
          .values({
            ...res.result,
            userId: user.id,
            carId: foundCar.id,
            plateNumber: toEnglishDigit(foundCar.number),
            nationalCode: toEnglishDigit(user.nationalCode),
            createdAt: new Date(),
            updatedAt: new Date(),
            client: InsuranceInquiryClient.site,
          })
          .execute();

        return await this.insuranceThirdPartyInquiryRepository.findOne(
          identifiers[0].id,
        );
      }
    } else {
      this.error.unprocessableEntity([
        res?.result || 'خطای غیرمنتظره ای رخ داده است',
      ]);
    }
  }

  /**
   * -------------------------------------------------------
   */
  async getRemainDaysToNewThirdParty(carId: number, user: UserEntity) {
    const insurance = await this.getThirdPartyInquiry(carId, user);

    let remain = { remainValue: 0, remainUnit: 'روز' };

    if (insurance) {
      const remainMonths = moment(insurance.expiredAt).diff(moment(), 'months');
      if (remainMonths > 0) {
        remain = { remainValue: remainMonths, remainUnit: 'ماه' };
      } else {
        const remainDays = moment(insurance.expiredAt).diff(moment(), 'days');
        remain = { remainValue: remainDays, remainUnit: 'روز' };
      }
    }

    return {
      ...remain,
      companyName: insurance?.companyName || null,
    };
  }

  /**
   * -------------------------------------------------------
   */
  async handleThirdPartyInquiryForValidCars() {
    const cars = await this.carService.getValidCarsWithNationalCode(100);

    const output = [];

    for (let i = 0; i < cars.length; i++) {
      const car = cars[i];

      const res = await retryOperation(
        () =>
          this.rayabimehService.thirdPartyInsuranceInquiry(
            toEnglishDigit(car.number),
            toEnglishDigit(car.user.nationalCode),
          ),
        1,
        1000,
      );

      if (res.success) {
        // There is no insurance
        if (res.result === null) {
          await this.carService.updateCar(car.id, {
            id: car.id,
            makeYear: '0',
          });

          output.push({
            carId: car.id,
            number: car.number,
            nationalCode: car.user.nationalCode,
            code: 0,
          });
        }
        // Has insurance
        else {
          await this.insuranceThirdPartyInquiryRepository
            .createQueryBuilder()
            .insert()
            .values({
              ...res.result,
              userId: car.userId,
              carId: car.id,
              plateNumber: toEnglishDigit(car.number),
              nationalCode: toEnglishDigit(car.user.nationalCode),
              createdAt: new Date(),
              updatedAt: new Date(),
              client: InsuranceInquiryClient.system,
            })
            .execute();

          output.push({
            carId: car.id,
            number: car.number,
            nationalCode: car.user.nationalCode,
            code: 200,
          });

          await this.carService.updateCar(car.id, {
            id: car.id,
            makeYear: res.result.carMakeYear,
            insuranceCarModel: res.result.carModelName,
          });
        }
      }
      // Error
      else {
        await this.carService.updateCar(car.id, {
          id: car.id,
          makeYear: '-1',
        });

        output.push({
          carId: car.id,
          number: car.number,
          nationalCode: car.user.nationalCode,
          code: -1,
          error: res.result,
        });
      }

      await sleep(5000);
    }

    return output;
  }
}
