import React, { useCallback, useEffect, useMemo } from 'react';
import { useApolloClient } from '@apollo/client';
import { Alert, Field, Form } from '@theorchard/suite-components';
import { PaymentType } from 'src/apollo/definitions/globalTypes';
import { PAID_BY_TOOLTIP_MSG } from 'src/apollo/type-constants/account';
import CurrencySelect from 'src/components/shared/currency-select';
import PaymentMethodDropdown from 'src/components/shared/payment-method-dropdown';
import PaymentScheduleSelect from 'src/components/shared/payment-schedule-select';
import { getCurrencyLabel } from 'src/utils/currency';
import { getPaymentEntityNameById } from 'src/utils/getPaymentEntityNameById';
import PaymentEntitySelect from '../shared/payment-entity-select';
import PayeeFieldsPayoneer from './payee-fields-payoneer';
import type { GetAccountFullDetailQuery } from 'src/apollo/queries/account/__generated__/get-account-full-detail';
import type { IErrors, IPayeeFormData } from 'src/types/payee-form';
import { usePayoneerProgramsList } from 'src/apollo/queries/payment-entity-payoneer-program';
import { getErrorMessage } from 'src/utils/form-validations/get-error-message';
import getErrorBody from 'src/apollo/errors';
import {
    normalizeDropdownOptions,
    UnnormalizedDropdownOption,
} from 'src/utils/normalize-dropdown-options';
import { useReferencePaymentEntity } from '@theorchard/accounting-apps-shared';

type AbacusAccount = NonNullable<GetAccountFullDetailQuery['abacusAccount']>;
type Contracts = AbacusAccount['contracts'][0];
type PaymentPendingInCurrentStatementPeriod =
    AbacusAccount['paymentPendingInCurrentStatementPeriod'];

export interface PayeeFieldsProps {
    changeHandler: (fieldName: keyof IPayeeFormData) => (value: any) => void;
    contracts: Contracts[] | null;
    errors?: IErrors;
    payeeFormData: IPayeeFormData;
    // Service name string from account data (e.g. "payoneer"), used as the
    // KNR payment-method label and for SAP field visibility check.
    paymentService?: string;
    paymentPendingInCurrentStatementPeriod: PaymentPendingInCurrentStatementPeriod | null;
    showMoveWarning?: boolean;
    setErrors: (error: IErrors) => void;
}

const PayeeFields: React.FC<PayeeFieldsProps> = ({
    changeHandler,
    contracts,
    errors,
    payeeFormData,
    paymentService,
    paymentPendingInCurrentStatementPeriod,
    showMoveWarning,
    setErrors,
}) => {
    const client = useApolloClient();
    const {
        data: payoneerProgramsList,
        loading: payoneerProgramsListLoading,
        error: payoneerProgramsListError,
    } = usePayoneerProgramsList(100, 0);

    const paymentEntityId =
        payeeFormData.paymentEntityId ||
        payeeFormData?.paymentEntity?.referencePaymentEntityId ||
        null;
    const { isKNR: isKNRSelected } = useReferencePaymentEntity(
        paymentEntityId,
        true
    );

    useEffect(() => {
        if (payoneerProgramsListError) {
            setErrors({
                serverError: getErrorBody(payoneerProgramsListError),
            });
        }
    }, [payoneerProgramsListError]);

    const accountPaymentEntityNameById =
        paymentEntityId &&
        getPaymentEntityNameById(paymentEntityId, client.cache);
    const showAdditionalInfo = Boolean(
        paymentEntityId && !accountPaymentEntityNameById
    );
    const isPaymentEntitySelectEnabled =
        Boolean(!payeeFormData?.paymentEntity?.referencePaymentEntityId) ||
        Boolean(
            payeeFormData?.agreementType === null && contracts?.length === 0
        );
    const paymentEntity =
        payeeFormData?.paymentEntityId?.toString() ||
        (payeeFormData?.paymentEntity
            ? payeeFormData?.paymentEntity?.referencePaymentEntityId?.toString()
            : '');

    const getProgramsByEntityIdAndCurrency = useCallback(
        (entityId: string, currency: string) => {
            const allItems =
                payoneerProgramsList?.abacusPaymentEntityPayoneerProgramsList
                    ?.items || [];

            const filteredItems = allItems.filter(item => {
                const isMatchingEntity =
                    item?.referencePaymentEntity?.referencePaymentEntityId ===
                    entityId;
                const supportedCurrencies =
                    item?.paymentCurrency
                        ?.split(',')
                        .map((c: string) => c.trim()) || [];
                const isMatchingCurrency =
                    !currency || supportedCurrencies.includes(currency);

                return isMatchingEntity && isMatchingCurrency;
            });
            // Returns unique Payoneer programs for a given payment entity,
            // deduplicated by referencePaymentTypeId so each type appears at most once
            const uniqueProgramsMap = new Map();

            filteredItems.forEach(item => {
                const typeId = item?.referencePaymentTypeId;
                if (typeId != null && !uniqueProgramsMap.has(typeId)) {
                    uniqueProgramsMap.set(typeId, item);
                }
            });

            return Array.from(uniqueProgramsMap.values());
        },
        [payoneerProgramsList]
    );

    // Builds dropdown options for the Payment Method field from programs
    // available for the currently selected payment entity and currency
    const paymentMethodOptions = useMemo(() => {
        return getProgramsByEntityIdAndCurrency(
            paymentEntity,
            payeeFormData.currencyCode
        )
            .map(item => ({
                label: item?.paymentService || '',
                value: item?.referencePaymentTypeId?.toString() || '',
            }))
            .filter(option => option.value !== '');
    }, [
        paymentEntity,
        payeeFormData.currencyCode,
        getProgramsByEntityIdAndCurrency,
    ]);

    const showSapField =
        isKNRSelected ||
        paymentMethodOptions
            .find(opt => opt.value === payeeFormData?.referencePaymentTypeId)
            ?.label?.toUpperCase() === PaymentType.SAP ||
        paymentService?.toUpperCase() === PaymentType.SAP ||
        false;

    const handlePaymentEntityChange = useCallback(
        (newEntityId: string) => {
            changeHandler('paymentEntityId')(newEntityId);

            // Resets referencePaymentTypeId and agreementTypeId if the selection is cleared
            if (!newEntityId) {
                changeHandler('referencePaymentTypeId')('');
                changeHandler('agreementTypeId')('');
                return;
            }

            const nextPrograms = getProgramsByEntityIdAndCurrency(
                newEntityId,
                payeeFormData.currencyCode
            );
            const hasCurrentMethod = nextPrograms.some(
                p =>
                    p?.referencePaymentTypeId?.toString() ===
                    payeeFormData.referencePaymentTypeId
            );

            // Resets referencePaymentTypeId and agreementTypeId if the new entity
            // doesn't support the currently selected payment method
            if (!hasCurrentMethod) {
                changeHandler('referencePaymentTypeId')('');
                changeHandler('agreementTypeId')('');
            }
        },
        [
            changeHandler,
            payeeFormData.referencePaymentTypeId,
            payeeFormData.currencyCode,
            getProgramsByEntityIdAndCurrency,
        ]
    );

    // Handles payment method selection by storing referencePaymentTypeId
    const handlePaymentMethodChange = useCallback(
        (newPaymentMethod: string) => {
            changeHandler('referencePaymentTypeId')(newPaymentMethod);

            // Clears agreementTypeId whenever the selection is cleared
            if (!newPaymentMethod) {
                changeHandler('agreementTypeId')('');
                return;
            }
        },
        [changeHandler]
    );

    // All programs for the selected payment entity + payment method + currency.
    // Used as the base for both allowedAgreementTypeIds and payoneerProgram lookup.
    // getProgramsByEntityIdAndCurrency is intentionally NOT used here because it deduplicates
    // by paymentService, losing programs with different agreement types / currencies.
    const programsForCurrentSelection = useMemo(() => {
        if (!paymentEntity || !payeeFormData.referencePaymentTypeId) {
            return [];
        }
        const allItems =
            payoneerProgramsList?.abacusPaymentEntityPayoneerProgramsList
                ?.items || [];

        return allItems.filter(item => {
            const supportedCurrencies =
                item?.paymentCurrency
                    ?.split(',')
                    .map((c: string) => c.trim()) || [];

            return (
                item?.referencePaymentEntity?.referencePaymentEntityId ===
                    paymentEntity &&
                item?.referencePaymentTypeId?.toString() ===
                    payeeFormData.referencePaymentTypeId &&
                supportedCurrencies.includes(payeeFormData.currencyCode)
            );
        });
    }, [
        paymentEntity,
        payeeFormData.referencePaymentTypeId,
        payeeFormData.currencyCode,
        payoneerProgramsList,
    ]);

    // All unique agreement type IDs available for the selected payment entity
    const allowedAgreementTypeIds = useMemo(() => {
        if (!paymentEntity) return undefined;

        const allItems =
            payoneerProgramsList?.abacusPaymentEntityPayoneerProgramsList
                ?.items || [];

        const ids = new Set<string>();
        allItems.forEach(item => {
            if (
                item?.referencePaymentEntity?.referencePaymentEntityId !==
                paymentEntity
            ) {
                return;
            }
            item?.referenceAgreementTypeId?.split(',')?.forEach(id => {
                const trimmed = id.trim();
                if (trimmed) ids.add(trimmed);
            });
        });

        return Array.from(ids);
    }, [paymentEntity, payoneerProgramsList]);

    // Resolves the Payoneer program by matching payment entity, payment method and agreement type.
    // Currency filtering is already applied upstream in programsForCurrentSelection.
    // paymentCurrency can also be a comma-separated list (e.g. "AUD,CAD,CHF,...").
    const payoneerProgram = useMemo(() => {
        const selectedAgreementTypeId =
            payeeFormData.agreementTypeId ||
            payeeFormData.agreementType?.referenceAgreementTypeId;

        if (!selectedAgreementTypeId) return null;

        const matchingItem = programsForCurrentSelection.find(item =>
            item?.referenceAgreementTypeId
                ?.split(',')
                ?.map((id: string) => id.trim())
                ?.includes(selectedAgreementTypeId)
        );
        return matchingItem?.payoneerProgram || null;
    }, [
        programsForCurrentSelection,
        payeeFormData.agreementTypeId,
        payeeFormData.agreementType,
    ]);

    useEffect(() => {
        changeHandler('payoneerProgram')(payoneerProgram);
    }, [payoneerProgram, changeHandler]);

    const paymentMethodDropdownProps = useMemo(() => {
        const currentReferencePaymentTypeId =
            payeeFormData?.referencePaymentTypeId;

        // Define Base Options for KNR vs Standard Flows
        // KNR (SAP) usually has a static selection, while standard entities use fetched programs.
        let options: UnnormalizedDropdownOption[] = isKNRSelected
            ? [{ value: currentReferencePaymentTypeId, label: paymentService }]
            : paymentMethodOptions;

        const hasCurrentId =
            currentReferencePaymentTypeId !== null &&
            currentReferencePaymentTypeId !== undefined &&
            currentReferencePaymentTypeId !== '';

        // Handle "Ghost Option" Logic
        // If the current value exists but is not present in the fetched options list,
        // we manually inject it as a disabled option to show the user their current selection while preventing it from being selected again.
        const isCurrentOptionMissing =
            !isKNRSelected &&
            hasCurrentId &&
            !paymentMethodOptions.some(
                opt => opt.value === currentReferencePaymentTypeId
            );

        if (isCurrentOptionMissing) {
            options = [
                ...paymentMethodOptions,
                {
                    value: currentReferencePaymentTypeId,
                    label: paymentService,
                    disabled: true, // Set to disabled as it is no longer a valid selection for this entity
                },
            ];
        }

        return {
            isClearable: !!currentReferencePaymentTypeId,
            onChange: (e: any) => handlePaymentMethodChange(e && e.value),
            isDisabled:
                payoneerProgramsListLoading ||
                !paymentEntity ||
                paymentMethodOptions.length === 0,
            options: normalizeDropdownOptions(options),
        };
    }, [
        payeeFormData.referencePaymentTypeId,
        paymentMethodOptions,
        paymentEntity,
        payoneerProgramsListLoading,
        paymentService,
        changeHandler,
        isKNRSelected,
        handlePaymentMethodChange,
    ]);

    return (
        <div className="PayeeForm-payee-form">
            <Field
                controlId="paymentCurrency"
                labelText="Payment Currency"
                message={getErrorMessage(errors?.currencyCode)}
            >
                <CurrencySelect
                    name=""
                    className="payeeCurrency"
                    isClearable={true}
                    id="payeeCurrency"
                    onChange={(e: { value: string }) =>
                        changeHandler('currencyCode')(e && e.value)
                    }
                    value={payeeFormData.currencyCode}
                    isDisabled={true}
                />
            </Field>
            <Field
                controlId="paymentSchedule"
                labelText="Payment Schedule"
                message={getErrorMessage(errors?.paymentSchedule)}
            >
                <PaymentScheduleSelect
                    className="paymentSchedule"
                    isClearable={true}
                    id="paymentSchedule"
                    name="paymentSchedule"
                    onChange={(e: any) =>
                        changeHandler('paymentSchedule')(e && e.value)
                    }
                    value={payeeFormData.paymentSchedule || ''}
                />
            </Field>
            <Field
                controlId="paymentMinimum"
                labelText="Payment Minimum"
                message={getErrorMessage(errors?.paymentMinimum)}
                note={
                    'currencyCode' in payeeFormData &&
                    getCurrencyLabel(payeeFormData.currencyCode)
                }
            >
                <Form.Control
                    className="paymentMinimum"
                    data-testid="input-payment-minimum"
                    id="paymentMinimum"
                    name="paymentMinimum"
                    onChange={({ target: { value: newPaymentMinimum } }) =>
                        changeHandler('paymentMinimum')(newPaymentMinimum)
                    }
                    type="text"
                    value={payeeFormData.paymentMinimum || '0.00'}
                />
            </Field>
            <hr />
            {showMoveWarning && (
                <Alert
                    className="PayeeForm-MoveProgramAlert"
                    title="Changes May Trigger Program Update"
                    text="Updating these fields may move the client to a new Payoneer program. Banking must be set up again before payments can be processed"
                    variant="information"
                    dismissible
                />
            )}

            <Field
                controlId="paymentEntity"
                labelText="Paid By"
                helpText={PAID_BY_TOOLTIP_MSG}
                message={getErrorMessage(errors?.paymentEntityId)}
            >
                <PaymentEntitySelect
                    className="paymentEntity"
                    isClearable={true}
                    isDisabled={!isPaymentEntitySelectEnabled}
                    id="paymentEntity"
                    name="paymentEntity"
                    onChange={(e: any) =>
                        handlePaymentEntityChange(e && e.value)
                    }
                    value={paymentEntity}
                    placeholder="Select Payment Entity"
                />
            </Field>
            <Field
                controlId="paymentMethod"
                labelText="Payment Method"
                message={getErrorMessage(errors?.referencePaymentTypeId)}
            >
                <PaymentMethodDropdown
                    name="paymentMethod"
                    className="paymentType"
                    id="paymentType"
                    value={payeeFormData?.referencePaymentTypeId}
                    {...paymentMethodDropdownProps}
                />
            </Field>
            <div className="paymentSection">
                {showSapField && (
                    <Field
                        controlId="sapVendorId"
                        labelText="SAP Vendor ID"
                        message={getErrorMessage(errors?.sapVendorId)}
                    >
                        <Form.Control
                            className="sapVendorId"
                            data-testid="input-sap-vendor-id"
                            id="sapVendorId"
                            name="sapVendorId"
                            onChange={({ target: { value: newSapVendorID } }) =>
                                changeHandler('sapVendorId')(newSapVendorID)
                            }
                            type="text"
                            value={payeeFormData.sapVendorId || ''}
                        />
                    </Field>
                )}
                <PayeeFieldsPayoneer
                    changeHandler={changeHandler}
                    agreementFieldProps={{
                        hidden: isKNRSelected,
                        disabled:
                            !paymentEntity || !allowedAgreementTypeIds?.length,
                        error: errors?.agreementTypeId,
                        // There is no Agreement Types for KNR,
                        // so we show all Agreement Types when KNR is selected
                        allowedAgreementTypeIds,
                        agreementTypeId: payeeFormData.agreementTypeId,
                    }}
                    data={{
                        payoneerProgram,
                        payoneerPayeeId: payeeFormData.payoneerPayeeId,
                        accountPayeeId: payeeFormData.accountPayeeId,
                        paymentPendingInCurrentStatementPeriod:
                            paymentPendingInCurrentStatementPeriod,
                    }}
                />
                {showAdditionalInfo && !payeeFormData.sapVendorId && (
                    <Alert
                        variant="information"
                        text="Additional information may be required based on Payment Method"
                    />
                )}
            </div>
            <hr />
            <Field
                controlId="payment-description"
                labelText="Payment Description"
                message={getErrorMessage(errors?.paymentDescription)}
                isOptional
            >
                <Form.Control
                    className="paymentDescription"
                    data-testid="input-payment-description"
                    id="paymentDescription"
                    name="paymentDescription"
                    onChange={({ target: { value: newPaymentDescription } }) =>
                        changeHandler('paymentDescription')(
                            newPaymentDescription
                        )
                    }
                    type="text"
                    as="textarea"
                    value={payeeFormData.paymentDescription || ''}
                />
            </Field>
        </div>
    );
};

export default PayeeFields;
