import React, { useCallback, useEffect, useMemo, useState } from 'react';
import {
    Button,
    GridTable,
    type GridTableColumnDefinition,
    Modal,
    SegmentedButton,
    Label,
    Alert,
} from '@theorchard/suite-components';
import { GlyphIcon } from '@theorchard/suite-icons';
import { useContractById } from 'src/apollo/queries/contract';
import {
    createUiState,
    getEarningsTransferDeleteColumnDef,
    RowUiState,
    TransferTableRow,
} from './contract-earnings-transfer-config-screen-columns';
import {
    createEmptyUiState,
    getEarningsTransferBaseColumnDefs,
    validateAmount,
} from './contract-earnings-transfer-config-screen-columns';
import type { EarningsTransferModalScreen } from 'src/apollo/type-constants/contract-earnings-transfer';
import { EARNINGS_TRANSFER_MODAL_SCREENS } from 'src/apollo/type-constants/contract-earnings-transfer';
import { useAccountChange } from 'src/hooks/contract-earnings-transfer/use-account-change';
import type { TransferRow } from 'src/types/abacus-contract-earnings-transfer';
import {
    AbacusEarningsTransferRateTypes,
    AbacusEarningsTransferSources,
    AbacusEarningsTransferTypes,
} from 'src/apollo/definitions/globalTypes';

const TransferProcessingOptions = {
    IN_SEQUENCE: 'In Sequence',
    IN_PARALLEL: 'In Parallel',
} as const;

type TransferProcessingOptions =
    (typeof TransferProcessingOptions)[keyof typeof TransferProcessingOptions];

const NEGATIVE_BALANCE_WARNING_MESSAGE =
    'If one of the transfers results in a negative closing balance, further transfers will not be processed under this configuration. To prevent this, select ‘Yes’ in the ‘Process Negative Closing Balances’ column.';

const TOTAL_PERCENTAGE_ERROR_MESSAGE =
    'The total percentage transferred from the same source exceeds 100%.';

// NOTE: The negative balance warning is shown if the transfers should be
// processed in sequence and if at least one row before the last one doesn't
// process negative balances
const shouldShowNegativeBalanceWarning = (
    rows: TransferRow[],
    processingOption: TransferProcessingOptions
): boolean => {
    const hasUpstreamRowNegative = !!rows
        .slice(0, rows.length - 1)
        .find(row => !row.negative);

    return (
        processingOption === TransferProcessingOptions.IN_SEQUENCE &&
        hasUpstreamRowNegative
    );
};

export const hasTotalPercentageError = (rows: TransferRow[]): boolean => {
    const percentage_by_source: Record<AbacusEarningsTransferSources, number> =
        {
            [AbacusEarningsTransferSources.CLOSING_BALANCE]: 0,
            [AbacusEarningsTransferSources.GROSS_REVENUE]: 0,
            [AbacusEarningsTransferSources.NET_REVENUE]: 0,
        };

    for (const row of rows) {
        if (
            row.input === undefined ||
            row.rateType != AbacusEarningsTransferRateTypes.PERCENT
        ) {
            continue;
        }

        percentage_by_source[row.input] += parseFloat(row.transferAmount);

        if (percentage_by_source[row.input] > 100) {
            return true;
        }
    }

    return false;
};

export interface ContractEarningsTransferConfigScreenProps {
    isVisible: boolean;
    isEditMode: boolean;
    rows: TransferRow[];
    editRow: TransferRow | null;
    transferType: AbacusEarningsTransferTypes | undefined;

    onRowChange: (index: number, updated: Partial<TransferRow>) => void;
    onAddRow: () => void;
    onDeleteRow: (index: number) => void;
    onResetRows: () => void;

    setModalScreen: (screen: EarningsTransferModalScreen) => void;
}

export const ContractEarningsTransferConfigScreen: React.FC<
    ContractEarningsTransferConfigScreenProps
> = ({
    isVisible,
    isEditMode,
    rows,
    editRow,
    transferType,
    onRowChange,
    onAddRow,
    onDeleteRow,
    onResetRows,
    setModalScreen,
}) => {
    const fromContractId = rows[0]?.fromContractId;
    const { data: fromContractData } = useContractById(fromContractId ?? '');
    const fromContract = fromContractData?.abacusContract;

    const [backModalOpen, setBackModalOpen] = useState(false);
    const closeBackModal = () => {
        setBackModalOpen(false);
    };

    const [rowUiStates, setRowUiStates] = useState<RowUiState[]>(() =>
        rows.map(createEmptyUiState)
    );

    const [processingOption, setProcessingOption] =
        useState<TransferProcessingOptions>(
            TransferProcessingOptions.IN_SEQUENCE
        );

    function resetChanges() {
        onResetRows();
        setRowUiStates([createEmptyUiState()]);
    }

    // Sync UI state array length when rows are added
    useEffect(() => {
        setRowUiStates(prev => {
            if (prev.length < rows.length) {
                const extras = rows.slice(prev.length).map(createEmptyUiState);
                return [...prev, ...extras];
            }
            return prev;
        });
    }, [rows.length]);

    useEffect(() => {
        if (editRow) {
            onRowChange(0, editRow);
            setRowUiStates([createUiState(editRow)]);
        }
    }, [editRow]);

    // Re-validate amount errors when rateType changes
    const rateTypesKey = rows.map(r => r.rateType ?? '').join(',');
    useEffect(() => {
        setRowUiStates(prev =>
            prev.map((uiState, i) => {
                const row = rows[i];
                if (row && row.transferAmount !== '') {
                    return {
                        ...uiState,
                        amountError: validateAmount(
                            row.transferAmount,
                            row.rateType
                        ),
                    };
                }
                return uiState;
            })
        );
    }, [rateTypesKey]);

    const updateRowUiState = useCallback(
        (index: number, updated: Partial<RowUiState>) => {
            setRowUiStates(prev =>
                prev.map((s, i) => (i === index ? { ...s, ...updated } : s))
            );
        },
        []
    );

    const handleDeleteRow = useCallback(
        (index: number) => {
            setRowUiStates(prev => prev.filter((_, i) => i !== index));
            onDeleteRow(index);
        },
        [onDeleteRow]
    );

    const selectedContractIds = useMemo(() => {
        const toContractIds = rows
            .map(row => row.toContractId)
            .filter(contractId => contractId !== undefined);

        return [rows[0].fromContractId, ...toContractIds];
    }, [rows]);

    const handleAccountChange = useAccountChange({
        selectedContractIds,
        onRowChange,
        updateRowUiState,
    });

    const handleProcessingOptionChange = (
        option: TransferProcessingOptions
    ) => {
        setProcessingOption(option);

        rows.forEach((_, index) => {
            onRowChange(index, {
                useStaticBalance:
                    option === TransferProcessingOptions.IN_PARALLEL
                        ? true
                        : false,
            });
        });
    };

    const tableData = useMemo<TransferTableRow[]>(
        () =>
            rows.map((row, i) => ({
                ...row,
                transferType,
                rowIndex: i,
                uiState: rowUiStates[i] ?? createEmptyUiState(),
            })),
        [rows, rowUiStates]
    );

    const columnDefs = useMemo(
        () =>
            getEarningsTransferBaseColumnDefs({
                fromContract,
                onRowChange,
                handleAccountChange,
                updateRowUiState,
            }),
        [onRowChange, handleAccountChange, updateRowUiState, fromContract]
    );

    const deleteColumn:
        | GridTableColumnDefinition<TransferTableRow>
        | undefined =
        rows.length > 1
            ? getEarningsTransferDeleteColumnDef(handleDeleteRow)
            : undefined;

    const showNegativeBalanceWarning = shouldShowNegativeBalanceWarning(
        rows,
        processingOption
    );
    const showTotalPercentageError = hasTotalPercentageError(rows);

    if (!isVisible) return null;

    return (
        <div className="ContractEarningsTransferConfigScreen">
            <div className="ContractEarningsTransferConfigScreen-header">
                <div>
                    <div className="ContractEarningsTransferConfigScreen-header-title">
                        Transfer Pairs
                    </div>
                    <div className="ContractEarningsTransferConfigScreen-header-description">
                        Include the contracts below. The From Contract has been
                        prepopulated with the current contract that is being
                        viewed.
                    </div>
                </div>
                <div className="ContractEarningsTransferConfigScreen-header-buttons">
                    {!isEditMode && (
                        <div className="ContractEarningsTransferConfigScreen-header-buttons-process-toggle">
                            <Label text="Process" />
                            <SegmentedButton
                                variant="primary"
                                value={processingOption}
                                onChange={handleProcessingOptionChange}
                            >
                                <SegmentedButton.Text
                                    testId="earningsTransferInSequenceToggle"
                                    id={TransferProcessingOptions.IN_SEQUENCE}
                                >
                                    {TransferProcessingOptions.IN_SEQUENCE}
                                </SegmentedButton.Text>
                                <SegmentedButton.Text
                                    testId="earningsTransferInParallelToggle"
                                    id={TransferProcessingOptions.IN_PARALLEL}
                                >
                                    {TransferProcessingOptions.IN_PARALLEL}
                                </SegmentedButton.Text>
                            </SegmentedButton>
                        </div>
                    )}
                    {!isEditMode && (
                        <Button
                            variant="secondary"
                            onClick={onAddRow}
                            className="ContractEarningsTransferConfigScreen-addButton"
                            data-testid="earningsTransferAddRowButton"
                        >
                            <GlyphIcon name="plus" size={12} />
                            Add Transfer Pairs
                        </Button>
                    )}
                </div>
                {showNegativeBalanceWarning && (
                    <Alert
                        className="ContractEarningsTransferConfigScreen-header-alert"
                        testId="earningsTransferNegativeBalanceWarning"
                        variant="warn"
                        text={NEGATIVE_BALANCE_WARNING_MESSAGE}
                    />
                )}
                {showTotalPercentageError && (
                    <Alert
                        className="ContractEarningsTransferConfigScreen-header-alert"
                        testId="earningsTransferTotalPercentageError"
                        variant="error"
                        text={TOTAL_PERCENTAGE_ERROR_MESSAGE}
                    />
                )}
            </div>

            <GridTable
                name="EarningsTransfer"
                className="ContractEarningsTransferConfigScreen-table"
                bordered
                columnDefs={
                    deleteColumn ? [...columnDefs, deleteColumn] : columnDefs
                }
                data={tableData}
                rowKey="rowId"
                variant="zebra"
            />

            <Modal
                isOpen={backModalOpen}
                onRequestClose={closeBackModal}
                header="Confirm Transfer Type Change"
                confirmButtonProps={{
                    onClick: () => {
                        setModalScreen(
                            EARNINGS_TRANSFER_MODAL_SCREENS.SELECT_TYPE
                        );
                        resetChanges();
                        closeBackModal();
                    },
                }}
                confirmTitle="Change transfer type"
                cancelButtonProps={{
                    title: 'Continue editing',
                    onClick: closeBackModal,
                }}
            >
                Changing transfer types will remove all information previously
                entered.
            </Modal>

            <Button
                type="button"
                className="ContractEarningsTransferConfigScreen-backLink"
                onClick={() => setBackModalOpen(true)}
                data-testid="earningsTransferBackButton"
            >
                <GlyphIcon name="arrowLeft" size={12} />
                Back to Transfer Types
            </Button>
        </div>
    );
};
