import { useMemo } from 'react';
import { useQueries } from 'react-query';

import { useDebounce } from '@material-hu/hooks/useDebounce';

import useCommunityFeature from 'src/hooks/useCommunityFeature';
import { CommunityFeature } from 'src/types/communityFeatures';

import { microloansKeys } from '../queries';
import { simulateCollaboratorOffer } from '../services/microloanService';
import {
  type LoanOffer,
  type LoanPaymentPlan,
  type SimulateResponse,
} from '../types';
import {
  getEligiblePlanWeeks,
  isAmountWithinOfferBounds,
} from '../utils/buildLoanPaymentPlan';
import {
  collaboratorMinorUnitsToCurrency,
  currencyToCollaboratorMinorUnits,
} from '../utils/collaboratorAmount';

export const PAYMENT_PLAN_DEBOUNCE_MS = 400;

export type UseOfferPaymentPlanResult = {
  paymentPlan: LoanPaymentPlan | undefined;
  isPaymentPlanLoading: boolean;
  simulateByWeeks: ReadonlyMap<number, SimulateResponse> | undefined;
};

export function useOfferPaymentPlan(
  loanOffer: LoanOffer | undefined,
  amountMxn: number,
): UseOfferPaymentPlanResult {
  const canViewMicroloans = useCommunityFeature(
    CommunityFeature.VIEW_MICROLOANS,
  );
  const debouncedAmount = useDebounce(amountMxn, PAYMENT_PLAN_DEBOUNCE_MS);
  const isDebouncingAmount = amountMxn !== debouncedAmount;

  const eligibleWeeks = useMemo(() => {
    if (!loanOffer) {
      return [];
    }
    return getEligiblePlanWeeks(loanOffer, debouncedAmount);
  }, [loanOffer, debouncedAmount]);

  const simulateQueries = useQueries(
    loanOffer
      ? eligibleWeeks.map(weeks => ({
          queryKey: microloansKeys.simulate(
            loanOffer.id,
            debouncedAmount,
            weeks,
          ),
          queryFn: async () => {
            const data = await simulateCollaboratorOffer(loanOffer.id, {
              totalAmount: currencyToCollaboratorMinorUnits(debouncedAmount),
              installmentsChosen: weeks,
            });
            return { weeks, data } as const;
          },
          enabled:
            canViewMicroloans &&
            eligibleWeeks.length > 0 &&
            isAmountWithinOfferBounds(loanOffer, debouncedAmount),
          retry: 1,
        }))
      : [],
  );

  const { apiPlan, simulateByWeeks } = useMemo(() => {
    const map = new Map<number, SimulateResponse>();
    const options: LoanPaymentPlan['options'][number][] = [];
    for (const q of simulateQueries) {
      if (q.data) {
        map.set(q.data.weeks, q.data.data);
        options.push({
          weeks: q.data.weeks,
          weeklyPaymentMxn: collaboratorMinorUnitsToCurrency(
            q.data.data.amountPerCycle,
          ),
          totalPaymentMxn: collaboratorMinorUnitsToCurrency(
            q.data.data.totalToRepay,
          ),
        });
      }
    }
    options.sort((a, b) => a.weeks - b.weeks);
    const plan: LoanPaymentPlan | undefined =
      options.length > 0 ? { amountMxn: debouncedAmount, options } : undefined;
    return { apiPlan: plan, simulateByWeeks: map };
  }, [debouncedAmount, simulateQueries]);

  const isSimulateBusy =
    eligibleWeeks.length > 0 &&
    simulateQueries.some(q => q.isFetching || q.isLoading);

  return {
    paymentPlan: apiPlan,
    isPaymentPlanLoading: isDebouncingAmount || isSimulateBusy,
    simulateByWeeks,
  };
}
