import React, { useEffect, useRef, useState } from 'react';
import type { ReactElement } from 'react';
import { Modal, Sidecar, useToast } from '@theorchard/suite-components';
import { useFeatureFlag } from '@theorchard/suite-frontend';
import { isArray, isEmpty } from 'lodash-es';
import { useParams } from 'react-router-dom';
import { AbacusContractLifecycleStatus } from 'src/apollo/definitions/globalTypes';
import { useUpdateContract } from 'src/apollo/mutations/contract';
import { updateContractMessage } from 'src/apollo/type-constants/contract';
import { ContractLifecycleEditContractFrom } from 'src/components/contract-lifecycle-edit/contract-lifecycle-edit-contract-form';
import {
    CONTRACT_TYPES,
    USER_FEATURES,
    UPDATE_RUN_CONTROLLER_ALERT_EXPLANATION,
} from '../../constants';
import type { ContractEditGeneralInfo } from 'src/types/abacus-contract-lifecycle';
import type { GetContractQuery } from 'src/apollo/queries/contract/__generated__/get-contract';

type AbacusContract = NonNullable<GetContractQuery['abacusContract']>;
type AbacusContractAccount = NonNullable<AbacusContract['account']>;
type AbacusContractAccountContracts = NonNullable<
    AbacusContractAccount['contracts']
>;

export interface ContractLifecycleEditGeneralContractInformationPropsTypes {
    accountName: string;
    isSidecarOpen: boolean;
    onRequestCloseSidecar: () => void;
    contractGeneralInfo: ContractEditGeneralInfo;
}

export const ContractLifecycleEditGeneralContractInformation: React.FC<
    ContractLifecycleEditGeneralContractInformationPropsTypes
> = ({
    accountName,
    isSidecarOpen,
    onRequestCloseSidecar,
    contractGeneralInfo,
}) => {
    const { contractId } = useParams<{ contractId: string }>();
    const [contractEditInfo, setContractEditInfo] =
        useState<ContractEditGeneralInfo>({
            accountId: null,
            contractName: null,
            contractType: null,
            initialStartDate: null,
            executionDate: null,
            isExcludedFromAccountingRun: false,
            isPaythroughContract: false,
            isPrimaryContract: false,
            runControllerName: null,
            runControllerId: null,
        });
    const [isSaveButtonDisabled, setIsSaveButtonDisabled] =
        useState<boolean>(false);
    const [isUpdatingContract, setIsUpdatingContract] =
        useState<boolean>(false);
    const [error, setError] = useState<string | null>(null);
    const originalRunControllerId = useRef<string | null>(null);
    const runControllerUpdated =
        originalRunControllerId.current !== contractEditInfo.runControllerId;
    const [
        isRunControllerUpdateConfirmModalOpen,
        setIsRunControllerUpdateConfirmModalOpen,
    ] = useState(runControllerUpdated);

    const [shouldMarkAsPrimaryContract, setShouldMarkAsPrimaryContract] =
        useState<boolean>(false);
    const [shouldRemovePrimaryContract, setShouldRemovePrimaryContract] =
        useState<boolean>(false);
    const [existingPrimaryContractId, setExistingPrimaryContractId] = useState<
        string | null
    >(null);

    const [
        isPrimaryContractUpdateConfirmModalOpen,
        setIsPrimaryContractUpdateConfirmModalOpen,
    ] = useState(false);
    const [primaryContractUpdateMsg, setPrimaryContractUpdateMsg] =
        useState<ReactElement | null>(null);

    const toast = useToast();
    const updateContract = useUpdateContract(contractId);

    // ACC-9395: TODO remove FF abacus_primary_contract
    const isFeaturePrimaryContractEnabled = useFeatureFlag(
        USER_FEATURES.ABACUS_PRIMARY_CONTRACT
    );

    useEffect(() => {
        if (contractGeneralInfo) {
            setContractEditInfo({
                accountId: contractGeneralInfo?.accountId,
                contractName: contractGeneralInfo?.contractName,
                contractType: contractGeneralInfo?.contractType,
                executionDate: contractGeneralInfo?.executionDate,
                initialStartDate: contractGeneralInfo?.initialStartDate,
                isExcludedFromAccountingRun:
                    contractGeneralInfo?.isExcludedFromAccountingRun,
                isPaythroughContract: contractGeneralInfo?.isPaythroughContract,
                isPrimaryContract: contractGeneralInfo?.isPrimaryContract,
                runControllerId: contractGeneralInfo?.runControllerId,
                runControllerName: contractGeneralInfo?.runControllerName,
            });
            originalRunControllerId.current =
                contractGeneralInfo.runControllerId;
        }

        setError(null);
        setShouldMarkAsPrimaryContract(false);
        setShouldRemovePrimaryContract(false);
    }, [contractGeneralInfo]);

    useEffect(() => {
        const contractType = contractGeneralInfo.contractType;
        const isPrimaryContract = contractGeneralInfo.isPrimaryContract;
        if (
            isFeaturePrimaryContractEnabled &&
            contractType === CONTRACT_TYPES.DISTRIBUTION &&
            isPrimaryContract !== contractEditInfo.isPrimaryContract
        ) {
            let primaryContract =
                contractGeneralInfo?.contracts?.filter(
                    (contract: AbacusContractAccountContracts[0]) =>
                        contract.isPrimaryContract &&
                        contractGeneralInfo?.contractType ===
                            contract?.contractType
                ) || [];

            if (
                primaryContract[0]?.lifecycle?.lifecycleStatus ===
                AbacusContractLifecycleStatus.TERMINATED
            ) {
                setExistingPrimaryContractId(primaryContract[0].contractId);
                primaryContract = [];
            }

            if (!isEmpty(primaryContract)) {
                if (contractEditInfo?.isPrimaryContract) {
                    if (contractId !== primaryContract[0].contractId) {
                        setShouldMarkAsPrimaryContract(true);
                        setExistingPrimaryContractId(
                            primaryContract[0].contractId
                        );
                        setPrimaryContractUpdateMsg(
                            messageToMarkContractPrimary(
                                contractType,
                                primaryContract[0].contractName,
                                primaryContract[0].contractId
                            )
                        );
                    } else setShouldMarkAsPrimaryContract(false);
                } else {
                    if (contractId === primaryContract[0].contractId)
                        setShouldRemovePrimaryContract(true);
                }
            }
        }
    }, [contractEditInfo.isPrimaryContract]);

    const handleFormChange = (
        formField: string,
        inputValue: string | boolean | null | undefined
    ) => setContractEditInfo({ ...contractEditInfo, [formField]: inputValue });

    const onSaveHandler = async () => {
        setIsSaveButtonDisabled(true);
        setIsUpdatingContract(true);

        // eslint-disable-next-line @typescript-eslint/no-explicit-any
        const variables: any = {
            contractId,
            contractName: contractEditInfo?.contractName?.trim(),
            executionDate: contractEditInfo?.executionDate || null,
            initialStartDate: contractEditInfo?.initialStartDate || null,
            isExcludedFromAccountingRun:
                contractEditInfo?.isExcludedFromAccountingRun,
            runControllerId: contractEditInfo?.runControllerId,
            isPaythroughContract: contractEditInfo?.isPaythroughContract,
            isPrimaryContract: contractEditInfo?.isPrimaryContract,
        };

        if (existingPrimaryContractId && isFeaturePrimaryContractEnabled) {
            await updateContract({
                variables: {
                    contractId: existingPrimaryContractId,
                    isPrimaryContract: false,
                },
            }).catch(errors => {
                setIsSaveButtonDisabled(false);
                setIsUpdatingContract(false);
                if (isArray(errors)) setError(JSON.stringify(errors[0]));
                else setError(errors);
            });
        }

        updateContract({ variables })
            .then(() => {
                onRequestCloseSidecar();
                toast(
                    updateContractMessage(
                        contractEditInfo?.contractName?.trim()
                    )
                );
                setIsSaveButtonDisabled(false);
                setIsUpdatingContract(false);
            })
            .catch(errors => {
                setIsSaveButtonDisabled(false);
                setIsUpdatingContract(false);
                if (isArray(errors)) setError(errors[0]);
                else setError(errors);
            });
    };

    const messageToMarkContractPrimary = (
        contractType: string,
        contractName: string,
        contractId: string
    ) => (
        <div>
            A primary {contractType} contract{' '}
            <b>
                {contractName} ({contractId}){' '}
            </b>
            already exists for <b>{accountName}</b> account. <br /> Do you want
            to make this new contract primary and set the existing one as
            non-primary?
        </div>
    );

    return (
        <>
            <Sidecar
                className="EditContractGeneralInfoSidecar"
                confirmDisabled={isSaveButtonDisabled || isUpdatingContract}
                isOpen={isSidecarOpen}
                onRequestClose={
                    isUpdatingContract
                        ? () => false
                        : () => {
                              setIsSaveButtonDisabled(false);
                              onRequestCloseSidecar();
                          }
                }
                testId="EditContractInfoSidecar"
                title="General Information"
                onConfirm={() =>
                    runControllerUpdated
                        ? setIsRunControllerUpdateConfirmModalOpen(true)
                        : shouldMarkAsPrimaryContract
                          ? setIsPrimaryContractUpdateConfirmModalOpen(true)
                          : onSaveHandler()
                }
            >
                <ContractLifecycleEditContractFrom
                    contract={contractEditInfo}
                    disableSaveButton={(flag: boolean) =>
                        setIsSaveButtonDisabled(flag)
                    }
                    errorMsg={error}
                    handleFormChange={handleFormChange}
                    shouldRemovePrimaryContract={shouldRemovePrimaryContract}
                />
            </Sidecar>
            <Modal
                className="RunControllerUpdateConfirmModal"
                title="Confirm Run Controller Change"
                confirmTitle="Apply Change"
                testId="runControllerUpdateConfirmModal"
                isOpen={isRunControllerUpdateConfirmModalOpen}
                onRequestClose={() =>
                    setIsRunControllerUpdateConfirmModalOpen(false)
                }
                onConfirm={() => {
                    setIsRunControllerUpdateConfirmModalOpen(false);
                    if (shouldMarkAsPrimaryContract)
                        setIsPrimaryContractUpdateConfirmModalOpen(true);
                    else onSaveHandler();
                    originalRunControllerId.current =
                        contractEditInfo.runControllerId;
                }}
            >
                {UPDATE_RUN_CONTROLLER_ALERT_EXPLANATION}
            </Modal>
            {isFeaturePrimaryContractEnabled && shouldMarkAsPrimaryContract && (
                <Modal
                    className="PrimaryContractUpdateConfirmModal"
                    title="Set New Contract as Primary"
                    confirmTitle="Apply Change"
                    testId="primaryContractUpdateConfirmModal"
                    isOpen={isPrimaryContractUpdateConfirmModalOpen}
                    onRequestClose={() => {
                        setIsPrimaryContractUpdateConfirmModalOpen(false);
                    }}
                    onConfirm={() => {
                        setIsPrimaryContractUpdateConfirmModalOpen(false);
                        onSaveHandler();
                    }}
                >
                    {primaryContractUpdateMsg}
                </Modal>
            )}
        </>
    );
};
