import React, { useEffect, useState, useMemo, useCallback } from 'react';
import {
    ToastProvider,
    GridTable,
    Alert,
    GlyphButton,
    useToast,
} from '@theorchard/suite-components';
import { useUpdatePaymentGroup } from 'src/apollo/mutations/payment-group-list';
import { usePaymentGroupsList } from 'src/apollo/queries/payment-group';
import PaymentNewGroupForm from 'src/components/payment-group/payment-new-group-form';
import NewPaymentGroupForm from 'src/components/payment-group/new-payment-group-form';
import './styles.scss';
import { EditableCell } from 'src/components/shared/editable-cell'; // Import EditableCell
import {
    DEFAULT_ITEMS_PER_PAGE,
    NO_PAYMENTS_RESULTS,
    PAYMENT_SCHEDULE_MAP,
    USER_FEATURES,
} from 'src/constants';
import { getCurrencyLabel } from 'src/utils/currency';
import type { GridTableColumnDefinition } from '@theorchard/suite-components';
import { PopupModal } from 'src/components/shared/popup-modal';
import { useFeatureFlag } from '@theorchard/suite-frontend';
import getErrorBody from 'src/apollo/errors';

type PaymentGroupListProps = {
    isSidecarOpen: boolean;
    setIsSidecarOpen: (open: boolean) => void;
    isFeatureAdminPagesTsxEnabled: boolean;
};

interface PaymentGroupRow {
    id: string;
    groupName: string;
    currencyCodes: string[];
    referencePaymentEntities: {
        referencePaymentEntityId: string;
        paymentEntityName: string;
    }[];
    paymentSchedules: string[];
    referenceAgreementTypes: {
        agreementType: string;
    }[];
}

const cleanArray = <T,>(arr: (T | null)[] | null | undefined) =>
    (arr ?? []).filter(Boolean) as T[];

const PaymentGroupList: React.FC<PaymentGroupListProps> = ({
    isSidecarOpen,
    setIsSidecarOpen,
    isFeatureAdminPagesTsxEnabled,
}) => {
    // State to track which payment group is being archived
    // if paymentGroupIdToArchive is not null, the archive confirmation modal will open
    const [paymentGroupIdToArchive, setPaymentGroupIdToArchive] = useState<
        string | null
    >(null);
    const [page, setPage] = useState(0);
    const [pageSize, setPageSize] = useState(DEFAULT_ITEMS_PER_PAGE);
    const { updatePaymentGroup, loading: updatePaymentGroupLoading } =
        useUpdatePaymentGroup();
    const toast = useToast();
    const isFeatureAbacusCreateAndApprovePaymentsEnabled = useFeatureFlag(
        USER_FEATURES.ABACUS_CREATE_AND_APPROVE_PAYMENTS
    );
    const offset = page * pageSize;

    const { data, loading, error, refetch } = usePaymentGroupsList({
        reusableOnly: true,
        limit: pageSize,
        offset,
    });

    const [paymentGroupList, setPaymentGroupList] = useState<PaymentGroupRow[]>(
        []
    );
    const [totalItemCount, setTotalItemCount] = useState(0);

    useEffect(() => {
        if (data?.abacusPaymentGroups) {
            const items = data.abacusPaymentGroups.items ?? [];
            const formatted: PaymentGroupRow[] = items
                .filter((pg): pg is NonNullable<typeof pg> => !!pg)
                .map(pg => ({
                    id: pg.paymentGroupId,
                    groupName: pg.groupName,
                    currencyCodes: cleanArray(pg.groupCriteria?.currencyCodes),
                    referencePaymentEntities:
                        pg.groupCriteria?.referencePaymentEntities ?? [],
                    paymentSchedules: cleanArray(
                        pg.groupCriteria?.paymentSchedules
                    ),
                    referenceAgreementTypes:
                        pg.groupCriteria?.referenceAgreementTypes ?? [],
                }));
            setPaymentGroupList(formatted);
            setTotalItemCount(data.abacusPaymentGroups.totalCount);
        } else {
            setPaymentGroupList([]);
            setTotalItemCount(0);
        }
    }, [data]);

    const refetchCurrentPage = useCallback(() => {
        return refetch({ limit: pageSize, offset: page * pageSize });
    }, [refetch, pageSize, page]);

    const onArchiveHandler = async () => {
        // paymentGroupIdToArchive is null initially
        if (!paymentGroupIdToArchive) return;

        try {
            await updatePaymentGroup({
                variables: {
                    paymentGroupId: paymentGroupIdToArchive,
                    isReusable: false,
                },
            });
            await refetchCurrentPage();
        } catch (e) {
            // Get the error message from the ApolloError/Error object
            // it would be an array of strings if there are multiple errors
            const errorMessage = getErrorBody(e);
            // Format the error message as a string
            const message = Array.isArray(errorMessage)
                ? errorMessage.join('; ')
                : errorMessage;

            toast(message);
        } finally {
            // Set paymentGroupIdToArchive to null after the operation is complete to close the modal
            setPaymentGroupIdToArchive(null);
        }
    };

    const columnDefs = useMemo<GridTableColumnDefinition<PaymentGroupRow>[]>(
        () => [
            {
                name: 'groupName',
                title: 'Payment Group Name',
                Cell: ({ row }) => (
                    <EditableCell
                        // initial cell value shown in the table
                        value={row.data.groupName}
                        // save handler: update the payment group name in backend
                        onSave={async newName => {
                            await updatePaymentGroup({
                                variables: {
                                    paymentGroupId: row.data.id,
                                    groupName: newName,
                                },
                            });
                        }}
                        // after successful save: refetch current page of table data
                        afterSave={async () => {
                            await refetchCurrentPage();
                        }}
                        validationFn={val => val.length > 0}
                        validationError="Value must not be empty"
                    />
                ),
            },
            {
                name: 'currencyCodes',
                title: 'Payment Currency',
                Cell: ({ row }) => (
                    <>
                        {row.data.currencyCodes.length > 0
                            ? row.data.currencyCodes.map((code, i) =>
                                  code ? (
                                      <div key={`${code}-${i}`}>
                                          {getCurrencyLabel(code)}
                                      </div>
                                  ) : null
                              )
                            : 'ALL'}
                    </>
                ),
            },
            {
                name: 'referencePaymentEntities',
                title: 'Paid By',
                Cell: ({ row }) => (
                    <>
                        {row.data.referencePaymentEntities.length > 0
                            ? row.data.referencePaymentEntities.map(entity => (
                                  <div key={entity.referencePaymentEntityId}>
                                      {entity.paymentEntityName}
                                  </div>
                              ))
                            : 'ALL'}
                    </>
                ),
            },
            {
                name: 'paymentSchedules',
                title: 'Payment Schedule',
                Cell: ({ row }) => (
                    <>
                        {row.data.paymentSchedules.length > 0
                            ? row.data.paymentSchedules.map((schedule, i) =>
                                  schedule ? (
                                      <div key={`${schedule}-${i}`}>
                                          {PAYMENT_SCHEDULE_MAP[schedule]}
                                      </div>
                                  ) : null
                              )
                            : 'ALL'}
                    </>
                ),
            },
            {
                name: 'referenceAgreementTypes',
                title: 'Agreement Type',
                Cell: ({ row }) => (
                    <>
                        {row.data.referenceAgreementTypes.length > 0
                            ? row.data.referenceAgreementTypes.map(
                                  (type, i) => (
                                      <div key={`${type.agreementType}-${i}`}>
                                          {type.agreementType}
                                      </div>
                                  )
                              )
                            : 'ALL'}
                    </>
                ),
            },
            // Do not render archivePaymentGroup column if feature flag is not enabled
            ...(isFeatureAbacusCreateAndApprovePaymentsEnabled
                ? [
                      {
                          name: 'archivePaymentGroup',
                          title: '',
                          align: 'right' as const,
                          Cell: ({
                              row,
                          }: {
                              row: { data: PaymentGroupRow };
                          }) => (
                              <GlyphButton
                                  variant="secondary"
                                  name="archive"
                                  tooltip="Archive payment group"
                                  onClick={() =>
                                      setPaymentGroupIdToArchive(row.data.id)
                                  }
                                  testId="ArchivePaymentGroupButton"
                              />
                          ),
                      },
                  ]
                : []),
        ],
        [
            refetchCurrentPage,
            updatePaymentGroup,
            isFeatureAbacusCreateAndApprovePaymentsEnabled,
        ]
    );

    return (
        <div className="PaymentGroupList" data-testid="PaymentGroupListTestId">
            {error && (
                <Alert
                    variant="error"
                    text="Failed to load payment groups. Please try again later."
                />
            )}

            <GridTable<PaymentGroupRow>
                aria-label="Payment group list"
                columnDefs={columnDefs}
                data={paymentGroupList}
                loading={loading}
                paginated
                page={page}
                pageSize={pageSize}
                totalCount={totalItemCount}
                onPageChange={setPage}
                onPageSizeChange={size => {
                    setPageSize(size);
                    setPage(0);
                }}
                testId="GridTableTestId"
                variant="zebra"
            />

            {!loading && paymentGroupList.length === 0 && !error && (
                <div
                    className="NoResultsFound"
                    role="status"
                    aria-live="polite"
                >
                    {NO_PAYMENTS_RESULTS}
                </div>
            )}

            <ToastProvider>
                {isFeatureAdminPagesTsxEnabled ? (
                    <NewPaymentGroupForm
                        isSidecarOpen={isSidecarOpen}
                        setIsSidecarOpen={setIsSidecarOpen}
                        refetchCurrentPage={refetchCurrentPage}
                    />
                ) : (
                    <PaymentNewGroupForm
                        setIsSidecarOpen={setIsSidecarOpen}
                        isSidecarOpen={isSidecarOpen}
                        refetchCurrentPage={refetchCurrentPage}
                    />
                )}
                {/* Do not render archive modal if feature flag is not enabled*/}
                {isFeatureAbacusCreateAndApprovePaymentsEnabled && (
                    <PopupModal
                        headerLabel="Archive this Payment Group?"
                        isOpen={!!paymentGroupIdToArchive}
                        closeHandler={() => {
                            // do not close the modal if the update is in progress
                            if (!updatePaymentGroupLoading)
                                setPaymentGroupIdToArchive(null);
                        }}
                        saveButtonText="Yes"
                        closeButtonText="No"
                        saveHandler={onArchiveHandler}
                        className={'PaymentGroupList-archiveModal'}
                        isSaveButtonDisabled={updatePaymentGroupLoading}
                        modalContent={
                            <Alert
                                variant="warn"
                                title="If you archive this Payment Group, it can no longer be linked with new payments."
                                text="Even if a new Payment Group with the same criteria is created, it will be considered different. Are you sure you want to continue to archive this Payment Group?"
                            />
                        }
                    />
                )}
            </ToastProvider>
        </div>
    );
};

export default PaymentGroupList;
