/* eslint-disable implicit-arrow-linebreak */
/* eslint-disable unicorn/consistent-function-scoping */
/* eslint-disable @typescript-eslint/naming-convention */
import { gql } from '@apollo/client';
import type { QueryFunctionContext } from '@tanstack/react-query';
import { calculateFinGAP } from 'api/core/query';
import type { RequestFinGAP } from 'api/core/types';
import type { Risk } from 'Components/Calculation/Form/Insurance/FinGAPTable/types';
import { STALE_TIME } from 'constants/request';
import dayjs from 'dayjs';
import utc from 'dayjs/plugin/utc';
import type * as CRMTypes from 'gql/crm.types';
import { comparer, reaction, toJS } from 'mobx';
import type { ReactionsContext } from 'process/types';
import { flatten } from 'tools/object';

dayjs.extend(utc);

export default function commonReactions({ store, apolloClient, queryClient }: ReactionsContext) {
  const { $calculation, $tables } = store;
  // Расчет итоговой суммы ФинГАП и запись в таблицу страхования
  reaction(
    () => $tables.fingap.totalSum,
    (totalSum) => {
      $tables.insurance.row('fingap').setValue('insCost', totalSum);
    }
  );

  /**
Реакция на изменение Страховой компании у ФинГАП и Срока лизинга:

Если Страхованя компания = Не выбрано, то

Плательщик insuredFinGAP = ЛП (100 000 000) и закрыто для редактирования,
Стоимость за первый период inscostFinGAP = 0
Срок страхования insTermFinGAP  = 12 мес (100 000 000) и закрыто для редактирования

иначе

Плательщик insuredFinGAP = открыто для редактирования,
Стоимость за первый период inscostFinGAP = 0
Срок страхования insTermFinGAP  = Если срок лизинга tbxLeasingPeriod < 13,
то указываем Срок страхования  insTermFinGAP  = 12 мес
и закрываем для редактирования, иначе открыто для редактирования
   */
  reaction(
    () => {
      const finGAPInsuranceCompany = $tables.insurance.row('fingap').getValue('insuranceCompany');
      const leasingPeriod = $calculation.element('tbxLeasingPeriod').getValue();

      return {
        finGAPInsuranceCompany,
        leasingPeriod,
      };
    },
    ({ finGAPInsuranceCompany, leasingPeriod }) => {
      if (!finGAPInsuranceCompany) {
        $tables.insurance
          .row('fingap')
          .setValue('insured', 100_000_000)
          .setValue('insCost', 0)
          .setValue('insTerm', 100_000_000)
          .block('insured')
          .block('insTerm');
      } else {
        $tables.insurance
          .row('fingap')
          .setValue('insured', 100_000_000)
          .setValue('insCost', 0)
          .setValue('insTerm', leasingPeriod < 13 ? 100_000_000 : null)
          .unblock('insured');

        if (leasingPeriod < 13) {
          $tables.insurance.row('fingap').block('insTerm');
        } else {
          $tables.insurance.row('fingap').unblock('insTerm');
        }
      }
    },
    {
      equals: comparer.shallow,
    }
  );

  // Очищаем таблицу ФинГАП, если не выбрана страховая компания
  reaction(
    () => $tables.insurance.row('fingap').getValue('insuranceCompany'),
    (finGAPInsuranceCompany) => {
      if (!finGAPInsuranceCompany) {
        $tables.fingap.clear();
      }
    }
  );

  const QUERY_GET_FINGAP_ADDPRODUCT_TYPES = gql`
    query GetFinGAPAddProductTypes($currentDate: DateTime) {
      evo_addproduct_types(
        statecode: 0
        evo_datefrom_param: { lte: $currentDate }
        evo_dateto_param: { gte: $currentDate }
        evo_product_type: 100000006
      ) {
        evo_addproduct_typeid
        evo_name
        evo_type_calc_cerebellum
        evo_cost_service_provider_withoutnds
        evo_addproduct_types {
          evo_addproduct_typeid
        }
      }
    }
  `;

  // Заполнение таблицы рисков ФинГАП + Запрос расчета финГАП в мозжечок

  reaction(
    () => {
      const finGAPInsuranceCompany = $tables.insurance.row('fingap').getValue('insuranceCompany');
      const paymentsValues = toJS($tables.payments.values);
      const plPriceRub = $calculation.$values.getValue('plPriceRub');
      const discountRub = $calculation.$values.getValue('discountRub');
      const firstPaymentRub = $calculation.element('tbxFirstPaymentRub').getValue();
      const leasingPeriod = $calculation.element('tbxLeasingPeriod').getValue();

      return {
        finGAPInsuranceCompany,
        paymentsValues,
        plPriceRub,
        discountRub,
        firstPaymentRub,
        leasingPeriod,
      };
    },
    async ({
      finGAPInsuranceCompany,
      paymentsValues,
      plPriceRub,
      discountRub,
      firstPaymentRub,
      leasingPeriod,
    }) => {
      if (!finGAPInsuranceCompany || $tables.payments.validation.hasErrors) return;

      const {
        data: { evo_addproduct_types },
      } = await apolloClient.query<
        CRMTypes.GetFinGapAddProductTypesQuery,
        CRMTypes.GetFinGapAddProductTypesQueryVariables
      >({
        query: QUERY_GET_FINGAP_ADDPRODUCT_TYPES,
        variables: {
          currentDate: dayjs().utc(false).format('YYYY-MM-DD'),
        },
      });

      const risks = evo_addproduct_types?.map(
        // prettier-ignore
        (evo_addproduct_type) => ({
            key: evo_addproduct_type?.evo_addproduct_typeid,
            riskId: evo_addproduct_type?.evo_addproduct_typeid,
            riskName: evo_addproduct_type?.evo_name,
            calcType: evo_addproduct_type?.evo_type_calc_cerebellum,
            premiumPerc: evo_addproduct_type?.evo_cost_service_provider_withoutnds,
            sum: 0,
            premium: 0,
            keys: evo_addproduct_type?.evo_addproduct_types?.map((k) => k?.evo_addproduct_typeid),
          } as Risk)
      );

      if (!risks) return;

      $tables.fingap.setRisks(risks);

      function getFingapRequestDataFromRisk(risk: Risk): RequestFinGAP {
        return {
          calcType: risk.calcType,
          payments: paymentsValues.map((payment) => ({
            percentPayment: payment,
          })),
          values: {
            plPrice: plPriceRub,
            discount: discountRub,
            firstPayment: firstPaymentRub,
            leasingPeriod,
            premiumPerc: risk.premiumPerc || 0,
          },
        };
      }

      function makeRequestGetFinGAP(request: RequestFinGAP) {
        const queryCalculateFinGAP = (context: QueryFunctionContext) =>
          calculateFinGAP(request, context);

        return queryClient.fetchQuery(
          ['core', 'fingap', ...flatten(request)],
          queryCalculateFinGAP,
          {
            staleTime: STALE_TIME,
          }
        );
      }

      Promise.all(
        risks
          .map((risk) => getFingapRequestDataFromRisk(risk))
          .map((data) => makeRequestGetFinGAP(data))
      ).then((results) => {
        const newRisks = risks.map((risk, i) => ({
          ...risk,
          sum: results.at(i)?.sum || 0,
          premium: results.at(i)?.premium || 0,
        }));

        $tables.fingap.setRisks(newRisks);
      });
    },
    {
      // Important: delay prohibits multiple reaction invocation
      equals: comparer.structural,
      delay: 100,
    }
  );
}
