import {
  Injectable,
  Logger,
  UnprocessableEntityException,
} from '@nestjs/common';
import { BaseDataService } from 'src/core/modules/data/service/base-data.service';
import {
  SalesPriceFormulaEntity,
  TransactionSetting,
} from '../../domain/entities/sales-price-formula.entity';
import { InjectRepository } from '@nestjs/typeorm';
import {
  SalesPriceFormulaModel,
  TransactionSettingModel,
} from '../models/sales-price-formula.model';
import { CONNECTION_NAME } from 'src/core/strings/constants/base.constants';
import { Repository } from 'typeorm';
import { FormulaType } from '../../constants';
import { TaxModel } from 'src/modules/transaction/tax/data/models/tax.model';
import { ItemModel } from 'src/modules/item-related/item/data/models/item.model';
import { TransactionModel } from 'src/modules/transaction/transaction/data/models/transaction.model';
import { CouchService } from 'src/modules/configuration/couch/data/services/couch.service';
import { TransactionType } from 'src/modules/transaction/transaction/constants';

@Injectable()
export class SalesPriceFormulaDataService extends BaseDataService<SalesPriceFormulaEntity> {
  constructor(
    @InjectRepository(SalesPriceFormulaModel, CONNECTION_NAME.DEFAULT)
    private repo: Repository<SalesPriceFormulaModel>,
    @InjectRepository(TaxModel, CONNECTION_NAME.DEFAULT)
    private tax: Repository<TaxModel>,
    @InjectRepository(ItemModel, CONNECTION_NAME.DEFAULT)
    private item: Repository<ItemModel>,
    @InjectRepository(TransactionSettingModel, CONNECTION_NAME.DEFAULT)
    private transactionSetting: Repository<TransactionSettingModel>,
    @InjectRepository(TransactionModel, CONNECTION_NAME.DEFAULT)
    private transaction: Repository<TransactionModel>,
    private couchService: CouchService,
  ) {
    super(repo);
  }

  profitShareFormula() {
    return this.repo.find({
      where: {
        type: FormulaType.PROFIT_SHARE,
      },
    });
  }

  async sentToBlackHole() {
    const transactionSettingData = await this.transactionSetting.findOne({
      where: {},
    });
    const percentage = transactionSettingData?.value ?? 100;

    // const transactionPercentage = Math.floor(Math.random() * 100) + 1;
    const transactionPercentage = await this.dataSaveFactor();

    Logger.log(`Factor ${transactionPercentage} from ${percentage}`);

    return transactionPercentage > percentage;
  }

  async dataSaveFactor() {
    const today = new Date();
    today.setHours(0, 0, 0, 0);

    const todayTimestamp = today.getTime();

    const totalTransactions = parseInt(
      await this.transaction
        .createQueryBuilder('transaction')
        .select('SUM(transaction.payment_total_pay)', 'sum')
        .where('transaction.created_at > :timestamp', {
          timestamp: todayTimestamp,
        })
        .andWhere('transaction.type = :type', { type: TransactionType.COUNTER })
        .getRawOne()
        .then((result) => result.sum || 0),
    );

    const couchTransaction = await this.couchService.totalTodayTransactions();

    if (couchTransaction == 0) return 0;

    const factor = (totalTransactions / couchTransaction) * 100;

    return factor;
  }

  async itemTax(id: string) {
    const item = await this.item.findOne({
      relations: ['tenant'],
      where: {
        id,
      },
    });

    const tenantShareMargin = item?.tenant?.share_margin;
    const profitShare = (item?.share_profit ?? 0) / 100;
    const tenantShare = tenantShareMargin ? (100 - tenantShareMargin) / 100 : 0;

    return { profitShare, tenantShare };
  }

  async salesPriceFormula() {
    const salesFormula = await this.repo.findOne({
      where: {
        type: FormulaType.SALES_PRICE,
      },
    });

    const taxes = await this.tax.find();

    for (const tax of taxes) {
      salesFormula.formula_string = salesFormula.formula_string.replace(
        tax.name,
        `(${tax.formula_string})`,
      );
    }

    const counter = {};
    do {
      let isInfinite = false;
      for (const tax of taxes) {
        salesFormula.formula_string = salesFormula.formula_string.replace(
          `${tax.name}_value`,
          `(${tax.formula_string})`,
        );

        if (salesFormula.formula_string.includes(`${tax.name}_value`))
          counter[tax.name] = counter[tax.name] ? counter[tax.name] + 1 : 1;

        for (const count of Object.keys(counter)) {
          if (!isInfinite && counter[count] > 50) isInfinite = true;
        }
      }

      if (isInfinite) {
        throw new UnprocessableEntityException({
          message: 'Infinity Loop Formula, please fix formula',
          error: 'Infinity Loop Formula',
          meta: counter,
        });
      }
    } while (salesFormula.formula_string.includes('_value'));

    return salesFormula;
  }
}

@Injectable()
export class TransactionSettingDataService extends BaseDataService<TransactionSetting> {
  constructor(
    @InjectRepository(TransactionSettingModel, CONNECTION_NAME.DEFAULT)
    private repo: Repository<TransactionSettingModel>,
  ) {
    super(repo);
  }
}
