import React from 'react';
import styled from 'styled-components';
import * as yup from 'yup';
import Decimal from 'decimal.js';
import { FORM_TYPES } from '@tamanu/constants';
import {
  AutocompleteField,
  DateField,
  Field,
  Form,
  NumberField,
  useSuggester,
  useDateTime,
  TAMANU_COLORS,
  TextButton,
} from '@tamanu/ui-components';
import { formatDisplayPrice, generateReceiptNumber } from '@tamanu/utils/invoice';

import { TranslatedText } from '../../components/Translation';
import { ModalFormActionRow } from '../../components/ModalActionRow';
import { useCreatePatientPayment, useUpdatePatientPayment } from '../../api/mutations';
import { useDefaultPaymentMethodId } from './useDefaultPaymentMethodId';
import {
  PaymentFormCard,
  Header,
  Label,
  LabelRow,
  StyledModal,
  Text,
} from './PatientPaymentStyledComponents';

const DECIMAL_PLACES = 2;

const Total = styled.div`
  color: ${props => props.theme.palette.text.primary};
  font-size: 16px;
  font-weight: 500;
`;

const FormFields = styled.div`
  .label-field {
    display: none;
  }
`;

const PayBalanceButton = styled(TextButton)`
  color: ${props => props.theme.palette.primary.main};
  font-size: 14px;
  text-transform: none;
  transition: all 0.1s;
  margin-top: 15px;

  &:hover {
    text-decoration: underline;
  }
`;

const calculateMaxAllowedAmount = (editingPayment, patientPaymentRemainingBalance) => {
  const editingAmount = editingPayment?.amount
    ? new Decimal(editingPayment.amount)
    : new Decimal(0);

  return new Decimal(patientPaymentRemainingBalance)
    .add(editingAmount)
    .toDecimalPlaces(DECIMAL_PLACES);
};

const getValidationSchema = (editingPayment, patientPaymentRemainingBalance) =>
  yup.object().shape({
    date: yup.string().required(<TranslatedText stringId="general.required" fallback="Required" />),
    methodId: yup
      .string()
      .required(<TranslatedText stringId="general.required" fallback="Required" />),
    amount: yup
      .string()
      .required(<TranslatedText stringId="general.required" fallback="Required" />)
      .test(
        'is-not-zero',
        <TranslatedText
          stringId="invoice.payment.validation.zeroAmount"
          fallback="Amount must be greater than 0"
          data-testid="translatedtext-6xjc"
        />,
        function (value) {
          try {
            return !new Decimal(value).isZero();
          } catch {
            return false;
          }
        },
      )
      .test(
        'is-valid-amount',
        <TranslatedText
          stringId="invoice.payment.validation.exceedAmount"
          fallback="Amount cannot be above patient total due"
          data-testid="translatedtext-dzh7"
        />,
        function (value) {
          try {
            const inputAmount = new Decimal(value);
            const maxAllowed = calculateMaxAllowedAmount(
              editingPayment,
              patientPaymentRemainingBalance,
            );
            return inputAmount.lessThanOrEqualTo(maxAllowed);
          } catch {
            return false;
          }
        },
      ),
  });

const isZeroAmount = amount => {
  if (amount === '' || amount === null || amount === undefined) return false;
  try {
    return new Decimal(amount).isZero();
  } catch {
    return false;
  }
};

// Calculates the patient's remaining balance after applying the payment amount
export const calculateDisplayedBalance = ({
  patientPaymentRemainingBalance,
  amount,
  paymentRecord = {},
}) => {
  if (amount === '' || amount === null || amount === undefined) {
    return patientPaymentRemainingBalance;
  }

  const decimalRemaining = new Decimal(patientPaymentRemainingBalance);
  const isEditMode = !!paymentRecord.id;

  return isEditMode
    ? decimalRemaining
      .plus(paymentRecord.amount || 0)
      .minus(amount)
      .toNumber()
    : decimalRemaining.minus(amount).toNumber();
};

export const PatientPaymentModal = ({
  isOpen,
  onClose,
  invoice,
  patientPaymentRemainingBalance,
  selectedPaymentRecord,
}) => {
  const { getCurrentDate } = useDateTime();
  const paymentRecord = selectedPaymentRecord ?? {};

  const isEditMode = !!paymentRecord.id;
  const validationSchema = getValidationSchema(paymentRecord, patientPaymentRemainingBalance);

  const paymentMethodSuggester = useSuggester('paymentMethod');
  const { defaultMethodId, loading: loadingDefaultMethod } = useDefaultPaymentMethodId(paymentMethodSuggester);
  const { mutate: createPatientPayment } = useCreatePatientPayment(invoice);
  const { mutate: updatePatientPayment } = useUpdatePatientPayment(invoice, paymentRecord.id);

  const handleSubmit = data => {
    const formattedAmount = new Decimal(data.amount).toDecimalPlaces(DECIMAL_PLACES).toString();

    const paymentData = {
      ...data,
      amount: formattedAmount,
    };

    const mutation = isEditMode ? updatePatientPayment : createPatientPayment;
    const submitData = isEditMode
      ? paymentData
      : { ...paymentData, receiptNumber: generateReceiptNumber() };

    mutation(submitData, { onSuccess: onClose });
  };

  return (
    <StyledModal
      title={
        isEditMode ? (
          <TranslatedText
            stringId="invoice.modal.editPatientPayments.title"
            fallback="Edit patient payments"
          />
        ) : (
          <TranslatedText stringId="invoice.modal.recordPayment.title" fallback="Record payment" />
        )
      }
      open={isOpen}
      onClose={onClose}
      data-testid="modal-j1bi"
    >
      {loadingDefaultMethod ? null : <Form
        enableReinitialize
        suppressErrorDialog
        onSubmit={handleSubmit}
        validationSchema={validationSchema}
        initialValues={{
          date: paymentRecord.date || getCurrentDate(),
          methodId: paymentRecord.patientPayment?.methodId ?? defaultMethodId ?? '',
          amount: paymentRecord.amount != null ? paymentRecord.amount : '',
          receiptNumber: paymentRecord.receiptNumber,
        }}
        formType={isEditMode ? FORM_TYPES.EDIT_FORM : FORM_TYPES.CREATE_FORM}
        data-testid="form-gsr7"
        render={({ setFieldValue, values }) => {
          const amount = values.amount;

          const balance = calculateDisplayedBalance({
            patientPaymentRemainingBalance,
            amount,
            paymentRecord,
          });

          const isNegativeDisplayAmount = new Decimal(balance).isNegative();

          // Validates amount input to allow only numbers with up to 2 decimal places
          const handleInputAmount = event => {
            const next = event.target.value;
            if (next !== '' && !/^\d*\.?\d{0,2}$/.test(next)) {
              event.target.value = values.amount || '';
            }
          };

          // Formats the amount to exactly 2 decimal places on blur
          const handleBlurAmount = () => {
            if (amount && !isNaN(amount)) {
              const formatted = new Decimal(amount).toFixed(DECIMAL_PLACES);
              setFieldValue('amount', formatted);
            }
          };

          // Sets the payment amount to the full remaining balance
          const handlePayBalance = () => {
            const fullBalance = isEditMode
              ? new Decimal(patientPaymentRemainingBalance).plus(paymentRecord.amount).toNumber()
              : patientPaymentRemainingBalance;

            setFieldValue('amount', String(fullBalance));
          };

          return (
            <>
              <Header>
                <Text>
                  {isEditMode ? (
                    <TranslatedText
                      stringId="invoice.modal.editPayment.instruction"
                      fallback="Edit patient payment below."
                    />
                  ) : (
                    <TranslatedText
                      stringId="invoice.modal.recordPayment.instruction"
                      fallback="Record a patient payment below."
                    />
                  )}
                </Text>
                <Total>
                  <TranslatedText
                    stringId="invoice.modal.recordPayment.totalDue"
                    fallback="Patient total due:"
                  />{' '}
                  <span style={{ color: isNegativeDisplayAmount ? TAMANU_COLORS.alert : null }}>
                    {formatDisplayPrice(balance)}
                  </span>
                </Total>
              </Header>
              <PaymentFormCard>
                <LabelRow>
                  <Label>
                    <TranslatedText stringId="invoice.modal.date" fallback="Date" />
                  </Label>
                  <Label>
                    <TranslatedText stringId="invoice.modal.method" fallback="Method" />
                  </Label>
                  <Label>
                    <TranslatedText stringId="invoice.modal.amount" fallback="Amount" />
                  </Label>
                </LabelRow>
                <FormFields>
                  <Field
                    name="date"
                    max={getCurrentDate()}
                    component={DateField}
                    data-testid="field-cx1w"
                  />
                  <Field
                    name="methodId"
                    component={AutocompleteField}
                    suggester={paymentMethodSuggester}
                    data-testid="field-c2nv"
                  />
                  <Field
                    name="amount"
                    component={NumberField}
                    onInput={handleInputAmount}
                    onBlur={handleBlurAmount}
                    min={0}
                    data-testid="field-773f"
                  />
                  <PayBalanceButton onClick={handlePayBalance}>
                    <TranslatedText stringId="invoice.modal.payBalance" fallback="Pay balance" />
                  </PayBalanceButton>
                </FormFields>
              </PaymentFormCard>
              <ModalFormActionRow
                onCancel={onClose}
                confirmDisabled={isZeroAmount(amount)}
                confirmText={
                  isEditMode ? (
                    <TranslatedText stringId="general.action.confirm" fallback="Confirm" />
                  ) : (
                    <TranslatedText
                      stringId="general.action.recordPayment"
                      fallback="Record payment"
                    />
                  )
                }
              />
            </>
          );
        }}
      />}
    </StyledModal>
  );
};
