import React, { useCallback, useEffect, useMemo, useState } from 'react';
import {
    Alert,
    Button,
    SkeletonLoader,
    Modal,
} from '@theorchard/suite-components';
import { get, isEmpty } from 'lodash-es';
import { useHistory, useParams } from 'react-router-dom';
import { useUpdateAccountPayee } from 'src/apollo/mutations/account-payee';
import { useResyncPaymentReadiness } from 'src/apollo/mutations/account-payee';
import {
    useCreateAccountPaymentTerm,
    useUpdateAccountPaymentTerm,
} from 'src/apollo/mutations/account-payment-term';
import { useAccountFullDetail } from 'src/apollo/queries/account';
import PayeeFields from 'src/components/payee-form/payee-fields';
import {
    SAP_VENDOR_ID_ERROR_MESSAGE,
    UNIQUE_SAP_VENDOR_ID,
} from 'src/constants';
import { PageHeader } from 'src/components/shared/pageHeader';
import {
    selectPaymentTab,
    getAccountList,
    getAccountDetail,
} from 'src/urls/frontend-royalties';
import { payeeFormValidation } from 'src/utils/form-validations';
import type { GetAccountFullDetailQuery } from 'src/apollo/queries/account/__generated__/get-account-full-detail';
import type { IParam, IErrors, IPayeeFormData } from 'src/types/payee-form';
import { useAbacusPayoneerProgramMoveType } from 'src/apollo/queries/abacus-payoneer-program-move-type';
import { Page } from '@theorchard/suite-frontend';
import { ProgramMoveType } from 'src/apollo/definitions/globalTypes';
import TitleWithGlyphIcon from 'src/components/shared/modal/title-with-glyph-icon';
import getErrorBody from 'src/apollo/errors';
import { useReferencePaymentEntity } from '@theorchard/accounting-apps-shared';
import './styles.scss';

type AbacusAccount = NonNullable<GetAccountFullDetailQuery['abacusAccount']>;
type AccountPaymentTerm = NonNullable<AbacusAccount['accountPaymentTerm']>;
type Contracts = AbacusAccount['contracts'][0];

const PayeeForm = () => {
    const [errors, setErrors] = useState<IErrors | undefined>(undefined);
    const [isMoveProgramConfirmModalOpen, setIsMoveProgramConfirmModalOpen] =
        useState(false);

    const history = useHistory();
    const { accountId } = useParams<IParam>();
    let accountDetails: AbacusAccount | null = null;
    let paymentService: string | null | undefined = null;
    let referencePaymentTypeId: string | null | undefined = null;
    let accountPaymentTerm: AccountPaymentTerm | null = null;
    let isPayeeLoading = true;
    let accountPayeeId = '';
    let contracts: Contracts[] | null = null;

    const {
        createAccountPaymentTerm,
        loading: createAccountPaymentTermLoading,
    } = useCreateAccountPaymentTerm(parseInt(accountId, 10));
    const {
        updateAccountPaymentTerm,
        loading: updateAccountPaymentTermLoading,
    } = useUpdateAccountPaymentTerm(parseInt(accountId, 10));
    const { updateAccountPayee, loading: updateAccountPayeeLoading } =
        useUpdateAccountPayee({ accountPayeeId });
    const { resyncPaymentReadiness, loading: resyncPaymentReadinessLoading } =
        useResyncPaymentReadiness({ accountPayeeId }, { accountId });

    const isMutationLoading =
        createAccountPaymentTermLoading ||
        updateAccountPaymentTermLoading ||
        updateAccountPayeeLoading ||
        resyncPaymentReadinessLoading;

    const { data, loading } = useAccountFullDetail(accountId);
    if (data) {
        accountDetails = data.abacusAccount;
        accountPaymentTerm = accountDetails?.accountPaymentTerm || null;
        isPayeeLoading = loading;
        accountPayeeId = accountDetails?.accountPayee?.accountPayeeId || '';
        contracts = accountDetails?.contracts || null;
        paymentService = get(
            accountDetails,
            'accountPayee.referencePaymentType.paymentService'
        );
        referencePaymentTypeId = get(
            accountDetails,
            'accountPayee.referencePaymentType.referencePaymentTypeId'
        );
    }

    const [payeeFormData, setPayeeFormData] = useState<IPayeeFormData>({
        accountPayeeId: '',
        accountPaymentTermId: '',
        currencyCode: '',
        paymentEntity: null,
        paymentEntityId: null,
        payoneerPayeeId: null,
        payoneerProgram: null,
        paymentMinimum: null,
        paymentSchedule: '',
        referencePaymentTypeId: '',
        sapVendorId: null,
        paymentDescription: '',
    });
    const paymentEntityId =
        payeeFormData.paymentEntityId ||
        payeeFormData?.paymentEntity?.referencePaymentEntityId ||
        null;
    const { isKNR: isKNRSelected } = useReferencePaymentEntity(
        paymentEntityId,
        true
    );
    const {
        data: moveTypeData,
        loading: isPayoneerProgramMoveTypeLoading,
        error: moveTypeError,
    } = useAbacusPayoneerProgramMoveType(
        // from program
        accountDetails?.accountPayee?.payoneerProgram?.payoneerProgramId ??
            null,
        // to program
        payeeFormData?.payoneerProgram?.payoneerProgramId ?? null
    );
    const handleCloseMoveProgramConfirmModal = () =>
        setIsMoveProgramConfirmModalOpen(false);

    useEffect(() => {
        if (!isEmpty(accountDetails))
            setPayeeFormData({
                ...(accountPaymentTerm || ''),
                accountPayeeId:
                    accountDetails?.accountPayee?.accountPayeeId || '',
                accountPaymentTermId:
                    accountPaymentTerm?.accountPaymentTermId || '',
                currencyCode: accountPaymentTerm?.currencyCode || '',
                payoneerPayeeId:
                    accountDetails?.accountPayee?.payoneerPayeeId || '',
                payoneerProgram:
                    accountDetails?.accountPayee?.payoneerProgram || null,
                paymentMinimum: accountPaymentTerm?.paymentMinimum || null,
                paymentSchedule: accountPaymentTerm?.paymentSchedule || '',
                referencePaymentTypeId:
                    referencePaymentTypeId?.toString() || '',
                sapVendorId: accountDetails?.accountPayee?.sapVendorId || null,
                paymentEntity: accountPaymentTerm?.paymentEntity || null,
                paymentDescription:
                    accountDetails?.accountPayee?.paymentDescription || '',
                agreementTypeId:
                    accountDetails?.accountPaymentTerm?.agreementType
                        ?.referenceAgreementTypeId,
            });
    }, [accountDetails]);

    const displayServerError = useMemo(() => {
        if (moveTypeError) {
            return getErrorBody(moveTypeError);
        }

        return errors?.serverError;
    }, [moveTypeError, errors?.serverError]);

    const saveAccountPayee = useCallback(() => {
        const { referencePaymentTypeId, ...rest } = payeeFormData;
        const variables = {
            ...rest,
            ...(referencePaymentTypeId && { referencePaymentTypeId }),
            accountPayeeId: accountPayeeId,
        };
        updateAccountPayee({ variables })
            .then(response => {
                if (!response) {
                    return;
                }
                history.push(selectPaymentTab(accountId));
            })
            .catch(err => {
                if (err) {
                    Array.isArray(err) &&
                        err.map(e => {
                            const str = JSON.stringify(e);
                            if (str.includes(UNIQUE_SAP_VENDOR_ID)) {
                                setErrors({
                                    sapVendorId: SAP_VENDOR_ID_ERROR_MESSAGE,
                                });
                                return;
                            }
                            if (e.sapVendorId) {
                                setErrors({ sapVendorId: e.sapVendorId });
                                return;
                            }
                            setErrors({ serverError: JSON.stringify(err) });
                        });
                }
            });
    }, [accountPayeeId, payeeFormData, history, updateAccountPayee]);

    const saveAccountPaymentTerm = useCallback(async () => {
        const paymentEntityIdStr =
            payeeFormData?.paymentEntityId?.toString() ||
            accountPaymentTerm?.paymentEntity?.referencePaymentEntityId?.toString() ||
            '';

        if (payeeFormData.accountPaymentTermId) {
            const paymentMinimum =
                payeeFormData.paymentMinimum === null
                    ? '0.00'
                    : payeeFormData.paymentMinimum;
            const variables = {
                ...payeeFormData,
                paymentMinimum,
                paymentEntityId: paymentEntityIdStr,
                // empty is allowed for KNR, but we shouldn't send the field
                // to avoid validation error in the existing backend logic
                agreementTypeId: payeeFormData.agreementTypeId || undefined,
            };
            return await updateAccountPaymentTerm({ variables })
                .then(response => {
                    if (!response) return;

                    return payeeFormData.accountPaymentTermId;
                })
                .catch(err => {
                    const errorMessage = err[0]?.message || JSON.stringify(err);

                    setErrors({ serverError: errorMessage });

                    throw new Error(errorMessage);
                });
        }
        const variables = {
            ...payeeFormData,
            accountId,
            paymentEntityId: paymentEntityIdStr,
        };
        return await createAccountPaymentTerm({ variables })
            .then(response => {
                if (!response) return;

                return response.data?.abacusCreateAccountPaymentTerm
                    ?.accountPaymentTermId;
            })
            .catch(err => {
                const errorMessage = err[0]?.message || JSON.stringify(err);

                setErrors({ serverError: errorMessage });

                throw new Error(errorMessage);
            });
    }, [
        accountId,
        payeeFormData,
        accountPaymentTerm,
        createAccountPaymentTerm,
        updateAccountPaymentTerm,
    ]);

    const executeSave = useCallback(() => {
        saveAccountPaymentTerm()
            .then(() => saveAccountPayee())
            .then(
                async () =>
                    await resyncPaymentReadiness({
                        variables: { accountPayeeId },
                    })
            )
            .catch(() => {
                // Errors are already handled and set in state within saveAccountPaymentTerm
            });
    }, [
        saveAccountPaymentTerm,
        saveAccountPayee,
        resyncPaymentReadiness,
        accountPayeeId,
    ]);

    const shouldShowMoveWarning = useMemo(() => {
        return (
            moveTypeData?.abacusPayoneerProgramMoveType ===
            ProgramMoveType.RELEASE_AND_WARN
        );
    }, [moveTypeData]);

    const submitHandler = useCallback(() => {
        const payeeValidationError = payeeFormValidation(payeeFormData);

        if (!isEmpty(payeeValidationError)) {
            return setErrors(payeeValidationError);
        }

        if (shouldShowMoveWarning) {
            setIsMoveProgramConfirmModalOpen(true);
        } else {
            executeSave();
        }
    }, [payeeFormData, shouldShowMoveWarning, executeSave]);

    const payeeChangeHandler =
        (fieldName: keyof IPayeeFormData) => (value: any) =>
            setPayeeFormData(prev => {
                // Converts empty/falsy values to null
                const newValue = value || null;

                // Skips the state update if the value hasn't changed
                if (prev[fieldName] === newValue) {
                    return prev;
                }

                return {
                    ...prev,
                    [fieldName]: newValue,
                };
            });

    const cancelButtonLink = selectPaymentTab(accountId);

    return (
        <>
            <PageHeader
                title="Payment Details Edit"
                breadcrumbs={[
                    { label: 'Accounts', url: getAccountList },
                    {
                        label: data?.abacusAccount?.accountName,
                        url: getAccountDetail(accountId),
                    },
                    { label: 'Payment General' },
                ]}
            />
            <Page.Col sm={12} md={5} lg={4}>
                <div className="PayeeForm">
                    <div className="PayeeForm-TopAlertSlot">
                        {accountDetails?.paymentPendingInCurrentStatementPeriod && (
                            <Alert
                                variant="information"
                                testId="PendingPaymentAlert"
                                className="PendingPaymentAlert"
                                text={
                                    <span>
                                        A payment is currently in process for
                                        this Payee. Editing values will not
                                        change the payment status.
                                    </span>
                                }
                            />
                        )}
                        {displayServerError && (
                            <Alert variant="error" text={displayServerError} />
                        )}
                    </div>
                    {isPayeeLoading ? (
                        <div className={'PayeeForm-Loader'}>
                            {[...Array(7)].map((_, i) => (
                                <div key={i} className="PayeeForm-Loader-field">
                                    <SkeletonLoader
                                        numberOfItems={1}
                                        width={120}
                                        height={20}
                                        vertical
                                    />
                                    <SkeletonLoader
                                        numberOfItems={1}
                                        height={40}
                                        vertical
                                    />
                                </div>
                            ))}
                            <div className="PayeeForm-Loader-buttons">
                                <SkeletonLoader
                                    numberOfItems={2}
                                    height={40}
                                    width={100}
                                />
                            </div>
                        </div>
                    ) : (
                        <>
                            <PayeeFields
                                payeeFormData={payeeFormData}
                                changeHandler={payeeChangeHandler}
                                contracts={contracts}
                                errors={errors}
                                paymentService={paymentService || undefined}
                                paymentPendingInCurrentStatementPeriod={
                                    accountDetails?.paymentPendingInCurrentStatementPeriod ||
                                    null
                                }
                                showMoveWarning={shouldShowMoveWarning}
                                setErrors={setErrors}
                            />
                            <div className="FormField-btn-wrap">
                                <div className="PayeeForm-buttons">
                                    <Button
                                        size="lg"
                                        variant="secondary"
                                        href={cancelButtonLink}
                                    >
                                        Cancel
                                    </Button>
                                    <Button
                                        variant="primary"
                                        onClick={submitHandler}
                                        size="lg"
                                        disabled={
                                            !isKNRSelected &&
                                            (!!moveTypeError ||
                                                !payeeFormData.payoneerProgram)
                                        }
                                        loading={
                                            isMutationLoading ||
                                            isPayoneerProgramMoveTypeLoading
                                        }
                                    >
                                        Save
                                    </Button>
                                </div>
                            </div>
                        </>
                    )}
                </div>
            </Page.Col>
            {isMoveProgramConfirmModalOpen && (
                <Modal
                    className="PayeeForm-ConfirmMoveProgramModal"
                    testId="PayeeForm-ConfirmMoveProgramModal"
                    onRequestClose={handleCloseMoveProgramConfirmModal}
                    title={
                        <TitleWithGlyphIcon
                            title="Confirm Payoneer Program Change"
                            glyphIconProps={{ name: 'warning', size: 24 }}
                        />
                    }
                    confirmButtonProps={{
                        title: 'Confirm change',
                        onClick: () => {
                            executeSave();
                            handleCloseMoveProgramConfirmModal();
                        },
                    }}
                    isOpen
                >
                    Changes to these fields will trigger a new program for the
                    client. Banking must be re-added before payments can be
                    processed.
                </Modal>
            )}
        </>
    );
};

export default PayeeForm;
