import * as math from 'mathjs';
import { Equation, parse } from 'algebra.js';
import {
  BadRequestException,
  HttpStatus,
  UnprocessableEntityException,
} from '@nestjs/common';
import { apm } from 'src/core/apm';

export function calculateFormula(
  formula: string,
  variable: object,
  total: number,
  solveFor = 'dpp',
) {
  try {
    const x1 = math.simplify(formula, variable).toString();
    // console.log('Formula ', x1);
    const dppFormula = parse(x1);
    const totalFormula = parse(total.toString());
    const equation = new Equation(totalFormula, dppFormula);

    // console.log(equation.toString());
    const result = equation.solveFor(solveFor).toString();
    // console.log(result, 'formula');

    const value = math.evaluate(result);
    // console.log(value, 'value');

    return value;
  } catch (e) {
    apm.captureError(e);
    throw new BadRequestException({
      message: 'Wrong value',
      meta: {
        formula,
        variable,
        total,
        solveFor,
      },
    });
  }
}

export function calculateSalesFormula(
  formula: string,
  taxes: object[],
  total: number,
  throwError = false,
) {
  try {
    const mapTaxes = mappingTaxes(taxes, formula, total);
    let { value } = mapTaxes;
    const { variable, tax_datas } = mapTaxes;

    const x1 = math.simplify(formula, variable).toString();
    console.log('Formula ', x1);
    const dppFormula = parse(x1);
    const totalFormula = parse(total.toString());
    const equation = new Equation(totalFormula, dppFormula);

    console.log(equation.toString());
    const result = equation.solveFor('dpp').toString();
    console.log(result, 'formula');

    value = math.evaluate(result);
    console.log(value, 'value');

    return {
      dpp_value: value,
      tax_datas: tax_datas,
    };
  } catch (e) {
    returnError(throwError, e, taxes);
  }
}

export function calculateProfitFormula(
  formula: string,
  taxes: object[],
  total: number,
  profit_share = 0,
  throwError = false,
) {
  try {
    const mapTaxes = mappingTaxes(taxes, formula, total, profit_share);
    let { value } = mapTaxes;
    const { variable, tax_datas } = mapTaxes;
    console.log(formula, variable);
    const result = math.simplify(formula, variable).toString();
    console.log(result, 'formula');

    value = math.evaluate(result);
    console.log(value, 'value');

    return {
      dpp_value: value,
      tax_datas: tax_datas,
    };
  } catch (e) {
    returnError(throwError, e, taxes);
  }
}

function mappingTaxes(taxes, formula, total, profit_share = 0) {
  const value = 0;
  const variable = {};
  const tax_datas = [];
  const const_variable = ['profit_share', 'item_share', 'dpp'];

  const regex = /([a-zA-Z0-9_]+)/g;

  const matches: string[] = formula.match(regex);
  const uniqueMatches = new Set(matches);
  const keys = Array.from(uniqueMatches);

  for (const key of keys) {
    if (!const_variable.includes(key)) {
      const keyData = taxes.find((tax) => tax.name == key);
      if (!keyData) {
        variable[key] = key;
      } else {
        variable[key] = keyData.value / 100;
        tax_datas.push({
          tax_id: keyData.id,
          tax_name: keyData.name,
          tax_value: keyData.value,
          tax_total_value: (keyData.value / 100) * Number(total),
        });
      }
    } else {
      switch (key) {
        case 'profit_share':
          variable[key] = profit_share / 100;
          break;

        case 'item_share':
          variable[key] = profit_share / 100;
          break;

        case 'dpp':
          if (profit_share > 0) variable[key] = total;
          break;
        default:
          variable[key] = profit_share;
          break;
      }
    }
  }

  return {
    value: value,
    variable: variable,
    tax_datas: tax_datas,
  };
}

function returnError(throwError, e, taxes) {
  if (throwError) {
    throw new UnprocessableEntityException({
      statusCode: HttpStatus.UNPROCESSABLE_ENTITY,
      message: `Gagal! Formula error`,
      error: 'Unprocessable Entity',
    });
  } else {
    console.log(e);

    return {
      dpp_value: 0,
      tax_datas: taxes,
    };
  }
}
