import React, { useEffect, useMemo, useState } from 'react';
import { Alert, Button, GridTable, Form } from '@theorchard/suite-components';
import { Page } from '@theorchard/suite-frontend';
import { isEmpty } from 'lodash-es';
import { useUpdatePaymentMinimums } from 'src/apollo/mutations/payment-minimums';
import { usePaymentMinimums } from 'src/apollo/queries/payment-minimums';
import { PaymentMinimumsListAndEditColumns } from 'src/components/payment-minimums/payment-minimums-list-and-edit-columns';
import { DEFAULT_ITEMS_PER_PAGE } from 'src/constants';
import { deformat } from 'src/utils/amount-helpers';
import { paymentMinimumValidation } from 'src/utils/form-validations';
import { smartFormatter } from 'src/utils/amount-helpers';
import type { GenericObject } from 'src/types/generic-object';
import type {
    AbacusPaymentMinimum,
    AbacusPaymentMinimumRecordUpdate,
    FormattedPaymentMinimum,
    UpdatedPaymentMinimum,
} from 'src/types/payment-minimums';
import './styles.scss';

const PaymentMinimumsListAndEdit: React.FC = () => {
    const [changedMins, setChangedMins] = useState<UpdatedPaymentMinimum>({});
    const [editMode, setEditMode] = useState<boolean>(false);
    const [errors, setErrors] = useState<Partial<GenericObject>>({});
    const [rows, setRows] = useState<Record<string, FormattedPaymentMinimum>>(
        {}
    );
    const [isSaveDisabled, setIsSaveDisabled] = useState<boolean>(false);

    const { data, loading: isLoading } = usePaymentMinimums();
    const updatePaymentMinimums = useUpdatePaymentMinimums();

    const changeHandler = (e: React.ChangeEvent<HTMLInputElement>) => {
        const {
            target: { name, value },
        } = e;
        const [id, method] = name.split('-');

        const validation: Partial<GenericObject> = paymentMinimumValidation(
            deformat(value),
            name
        );

        if (!isEmpty(validation) && validation[name])
            return setErrors(validation);
        setErrors({});
        const record =
            rows[parseInt(id)][method as keyof FormattedPaymentMinimum];
        const newMin = deformat(record) === deformat(value) ? null : value;

        setChangedMins((prev: UpdatedPaymentMinimum) => ({
            ...prev,
            [id]: {
                ...prev[id],
                id,
                [method]: newMin,
            },
        }));
    };

    const getClassName = (id: string, method: string) => {
        const hasBeenChanged =
            changedMins[id] &&
            changedMins[id][method as keyof FormattedPaymentMinimum];

        if (!hasBeenChanged) return '';
        if (errors[`${id}-${method}`]) return 'error';

        const record =
            rows[parseInt(id)][method as keyof FormattedPaymentMinimum];
        return deformat(
            changedMins[id][method as keyof FormattedPaymentMinimum]
        ) === deformat(record)
            ? ''
            : 'updated';
    };

    const reset = () => {
        setRows({});
        setErrors({});
        setChangedMins({});
        setEditMode(false);
    };

    const submitHandler = () => {
        if (Object.keys(changedMins).length === 0) {
            setEditMode(false);
            setIsSaveDisabled(false);
            setChangedMins({});
            return;
        }
        setIsSaveDisabled(true);
        const updatedData = Object.values(changedMins).map(
            (min: Partial<FormattedPaymentMinimum>) => {
                const req: AbacusPaymentMinimumRecordUpdate = {
                    paymentMinimumId: min.id as keyof FormattedPaymentMinimum,
                };
                if (min.checkAmount)
                    req.checkAmount = deformat(min.checkAmount);
                return req;
            }
        );

        updatePaymentMinimums({ variables: { paymentMinimums: updatedData } })
            .then(() => {
                setEditMode(false);
                setIsSaveDisabled(false);
                setChangedMins({});
            })
            .catch(err => {
                setErrors({ serverError: err });
                setIsSaveDisabled(false);
            });
    };

    const formatPaymentMinimums = (
        items: Array<AbacusPaymentMinimum> | null
    ) => {
        return (
            items
                ?.filter((item): item is AbacusPaymentMinimum => !!item)
                .reduce(
                    (paymentMinimums, item) => {
                        paymentMinimums[item!.paymentMinimumId] = {
                            id: item!.paymentMinimumId,
                            currencyCode: item!.currencyCode,
                            currencyName: item!.currencyName,
                            checkAmount: smartFormatter(
                                item!.checkAmount,
                                item!.currencyCode
                            ),
                        };
                        return paymentMinimums;
                    },
                    {} as Record<string, FormattedPaymentMinimum>
                ) || {}
        );
    };

    useEffect(() => {
        if (data && !isEmpty(data.abacusPaymentMinimums)) {
            const { items } = data.abacusPaymentMinimums;
            const paymentMinimumsData = formatPaymentMinimums(items);
            setRows(paymentMinimumsData);
        }
    }, [data, editMode]);

    const columnDefs = useMemo(() => PaymentMinimumsListAndEditColumns(), []);

    const tableData = useMemo(() => {
        return Object.entries(rows)?.map(
            ([key, paymentMinimum]: [string, FormattedPaymentMinimum]) => ({
                currency: `${paymentMinimum.currencyCode} (${paymentMinimum.currencyName})`,
                minimum: (
                    <div className="PaymentMinimumsPageGrid-input" key={key}>
                        <Form.Control
                            className={getClassName(
                                paymentMinimum.id,
                                'checkAmount'
                            )}
                            data-testid="amount-input"
                            defaultValue={paymentMinimum.checkAmount}
                            disabled={!editMode || isSaveDisabled}
                            name={`${paymentMinimum.id}-checkAmount`}
                            onChange={(
                                e: React.ChangeEvent<HTMLInputElement>
                            ) => changeHandler(e)}
                            type="text"
                        />
                        {errors[`${paymentMinimum.id}-checkAmount`] && (
                            <div className="PaymentMinimumsPageGrid-input-error">
                                {errors[`${paymentMinimum.id}-checkAmount`]}
                            </div>
                        )}
                    </div>
                ),
            })
        );
    }, [rows, editMode, errors, isSaveDisabled]);

    return (
        <Page.Grid className="PaymentMinimumsPageGrid">
            <Page.Col>
                {errors.serverError && (
                    <Alert variant="error" text={errors.serverError} />
                )}
            </Page.Col>
            <Page.Col>
                <GridTable
                    bordered
                    columnDefs={columnDefs}
                    data={tableData}
                    loading={isLoading}
                    loadingRows={DEFAULT_ITEMS_PER_PAGE}
                    rowKey="id"
                    testId="PaymentMinimumsGridTable"
                    variant="zebra"
                />
            </Page.Col>
            <Page.Col>
                <div className="PaymentMinimumsPageGrid-buttons">
                    {editMode && (
                        <Button disabled={isSaveDisabled} onClick={reset}>
                            Cancel
                        </Button>
                    )}
                    <Button
                        disabled={
                            Object.keys(errors).length > 0 || isSaveDisabled
                        }
                        onClick={() =>
                            editMode ? submitHandler() : setEditMode(true)
                        }
                        loading={isSaveDisabled}
                        variant="primary"
                    >
                        {editMode ? 'Save' : 'Edit'}
                    </Button>
                </div>
            </Page.Col>
        </Page.Grid>
    );
};

export default PaymentMinimumsListAndEdit;
