import React, { useEffect, useReducer, useState } from 'react';
import {
    Alert,
    Button,
    getContinents,
    Modal,
    useToast,
} from '@theorchard/suite-components';
import { PageHeader as SuitePageHeader } from '@theorchard/suite-components';
import { capitalize, isEmpty, pick } from 'lodash-es';
import { useHistory, useParams } from 'react-router-dom';
import { TransactionTypeGroupAdmin } from 'src/apollo/definitions/globalTypes';
import {
    useCreateContractTerm,
    useSoftDeleteContractTerm,
    useUpdateContractTerm,
} from 'src/apollo/mutations/contract-term';
import {
    useCreateContractTermConditions,
    useDeleteContractTermConditions,
    useUpdateContractTermConditions,
} from 'src/apollo/mutations/contract-term-condition';
import { useContractTerm } from 'src/apollo/queries/contract-term';
import { useStoreList } from 'src/apollo/queries/stores';
import {
    useTransactionTypeGroups,
    useTransactionTypes,
} from 'src/apollo/queries/transaction-types';
import { ContractTermsDistroFormFields } from 'src/components/contract-terms-refactored/contract-terms-distro-form-fields';
import { LinkButton } from 'src/components/shared/link-button';
import {
    ADD_CONTRACT_TERM_CONDITION,
    CONTRACT_TERM_TYPES,
    DISTRO_CONTRACT_TERM_TYPES,
    EMPTY_TERM_CONDITIONS,
    RECEIVE_CONTINENTS,
    RECEIVE_CONTRACT_TERM,
    RECEIVE_STORES,
    RECEIVE_TRANSACTION_TYPE_GROUPS,
    RECEIVE_TRANSACTION_TYPES,
    SET_DEFAULT_CONDITION,
    USER_FEATURES,
} from 'src/constants';
import { ContractTermContext } from 'src/contexts/contract-term-context';
import { useContractBreadcrumbs } from 'src/hooks/breadcrumbs/contract';
import {
    initialTermState,
    reducer,
} from 'src/reducers/contract-term/contract-term-reducer';
import { ValidationErrorType } from 'src/types/validation';
import { getContractDetail } from 'src/urls/frontend-royalties';
import { formatConditions, updateConditions } from 'src/utils/contract-terms';
import { isFeatureFlagEnabled } from 'src/utils/feature-flag';
import {
    contractTermFormValidation,
    generateContractTermValidationErrorStrings,
} from 'src/utils/form-validations/contract-term-form-validation';
import { PageHeader } from '../shared/pageHeader';
import { ContractTermsDistroForm } from './contract-terms-distro-form';
import type { GetContractTermQuery } from 'src/apollo/queries/contract-term/__generated__/get-contract-term';
import './styles.scss';

type AbacusContractTerm = NonNullable<
    GetContractTermQuery['abacusContractTerm']
>;
type ContractTermCondition = AbacusContractTerm['conditions'] extends
    | (infer T)[]
    | null
    ? T
    : null;

export const ContractTermsDistro: React.FC = () => {
    const {
        contractId,
        contractTermId: termId,
        termType,
    } = useParams<{
        contractId: string;
        contractTermId: string;
        termType: string;
    }>();

    const [errors, setErrors] = useState<string[]>([]);
    const [attachmentErrors, setAttachmentErrors] = useState<string[]>([]);
    const [isSaving, setIsSaving] = useState(false);
    const [isModalOpen, setIsModalOpen] = useState(false);
    const [isFormValid, setIsFormValid] = useState(false);

    const isValidTermType = Object.values(DISTRO_CONTRACT_TERM_TYPES).includes(
        termType
    );
    const termTypeCapitalized = capitalize(termType);

    const [state, dispatch]: any = useReducer(reducer, {
        ...initialTermState,
        contractId,
        termType: termType,
        isBaseTerm: false,
    });

    const history = useHistory();
    const toast = useToast();
    const createContractTerm = useCreateContractTerm(contractId);
    const updateContractTerm = useUpdateContractTerm();
    const createContractTermConditions = useCreateContractTermConditions(
        contractId,
        termId
    );
    const updateContractTermConditions = useUpdateContractTermConditions(
        contractId,
        termId
    );
    const deleteContractTermConditions = useDeleteContractTermConditions(
        contractId,
        termId
    );
    const deleteContractTerm = useSoftDeleteContractTerm(contractId);

    const { data: storesData } = useStoreList();
    const { data: transactionTypeData } = useTransactionTypes();
    const { data: transactionTypeGroupData } = useTransactionTypeGroups(
        TransactionTypeGroupAdmin.CONTRACT_ADMIN
    );
    let contractTermData: any;
    if (termId) ({ data: contractTermData } = useContractTerm(termId));
    const pageTitle = `${termId ? 'Edit' : 'Add'} ${termTypeCapitalized} Term`;
    const breadcrumbs = useContractBreadcrumbs(pageTitle);

    const isFeatureCreateTermsWithNoTracksAndProducts = isFeatureFlagEnabled(
        USER_FEATURES.ABACUS_CREATE_TERMS_WITH_NO_TRACKS_AND_PRODUCTS
    );

    useEffect(() => {
        dispatch({
            type: RECEIVE_CONTINENTS,
            continents: getContinents(),
        });
    }, []);

    useEffect(() => {
        if (storesData)
            dispatch({
                type: RECEIVE_STORES,
                stores: storesData?.deliveryStoresV2,
            });
    }, [storesData]);

    useEffect(() => {
        if (transactionTypeData)
            dispatch({
                type: RECEIVE_TRANSACTION_TYPES,
                transactionTypes: transactionTypeData?.transactionTypes,
            });
    }, [transactionTypeData]);

    useEffect(() => {
        if (transactionTypeGroupData)
            dispatch({
                type: RECEIVE_TRANSACTION_TYPE_GROUPS,
                transactionTypeGroups:
                    transactionTypeGroupData?.transactionTypeGroups,
            });
    }, [transactionTypeGroupData]);

    useEffect(() => {
        if (!isValidTermType) {
            setErrors([`Invalid term type: ${termType}`]);
        }
    }, [isValidTermType]);

    const prepareAttachmentsRelations = (attachmentsRelations: any) => {
        const pickList: string[] = [];
        if (!attachmentsRelations) return null;
        ['labelIds', 'upcs'].forEach((key: string) =>
            attachmentsRelations[key] ? pickList.push(key) : null
        );
        if (isEmpty(pickList)) return null;
        return pick(attachmentsRelations, pickList);
    };

    useEffect(() => {
        if (
            contractTermData &&
            !isEmpty(contractTermData?.abacusContractTerm)
        ) {
            const {
                attachments,
                attachmentsRelations,
                conditions,
                contract,
                contractTermId,
                contractTermName,
                termType,
            } = contractTermData.abacusContractTerm;
            const contractTerm = {
                attachments,
                attachmentsRelations,
                contract,
                contractId,
                contractTermId,
                contractTermName,
                termType,
            };

            let existingLabelAttachments: string[] = [];
            let contractTermIds: string[] = [];
            if ([CONTRACT_TERM_TYPES.LABEL].includes(termType))
                existingLabelAttachments = attachments;

            if (
                [
                    CONTRACT_TERM_TYPES.PRODUCT,
                    CONTRACT_TERM_TYPES.TRACK,
                ].includes(termType)
            )
                existingLabelAttachments = attachmentsRelations?.labelIds;

            if (contract?.contractTerms)
                contractTermIds = contract?.contractTerms
                    ?.map((term: AbacusContractTerm) => term.contractTermId)
                    .filter(Boolean);

            dispatch({
                type: RECEIVE_CONTRACT_TERM,
                contractTerm,
                existingLabelAttachments,
                contractTermIds,
            });
            if (conditions.length > 0) {
                dispatch({ type: EMPTY_TERM_CONDITIONS });
                conditions.forEach((condition: ContractTermCondition) => {
                    const { countryCodes, storeIds, transactionTypeIds } =
                        state;
                    dispatch({
                        type: ADD_CONTRACT_TERM_CONDITION,
                        condition: {
                            ...condition,
                            conditions: formatConditions(
                                condition?.conditions,
                                countryCodes,
                                storeIds,
                                transactionTypeIds
                            ),
                        },
                    });
                });
            } else dispatch({ type: SET_DEFAULT_CONDITION });
        }
    }, [contractTermData, state.stores]);

    useEffect(() => {
        const validationErrors = generateContractTermValidationErrorStrings(
            state.attachmentsValidError
        );
        setAttachmentErrors(validationErrors);
    }, [state.attachmentsValidError]);

    useEffect(() => {
        const validationErrors = contractTermFormValidation(state, true, [
            ValidationErrorType.PRESENCE,
        ]);
        setIsFormValid(validationErrors.length === 0);
    }, [state]);

    const saveContractTerm = async () => {
        const {
            attachments,
            contractTermId,
            contractTermName,
            attachmentsRelations,
            termType,
        } = state;

        const updatedAttachmentsRelations =
            prepareAttachmentsRelations(attachmentsRelations);

        const requestMethod = contractTermId
            ? updateContractTerm
            : createContractTerm;

        let variables: any = {
            attachments,
            contractId,
            contractTermId:
                requestMethod === updateContractTerm ? contractTermId : null,
            contractTermName,
            isBaseTerm: false,
            termType,
        };

        if (updatedAttachmentsRelations)
            variables = {
                ...variables,
                attachmentsRelations: updatedAttachmentsRelations,
            };

        return await requestMethod({ variables }).then((response: any) => {
            if (!response) return;

            return contractTermId
                ? response?.data?.abacusUpdateContractTerm?.contractTermId
                : response?.data?.abacusCreateContractTerm?.contractTermId;
        });
    };

    const saveContractTermConditions = (contractTermId: string) => {
        const { countryCodes, storeIds, transactionTypeIds } = state;

        const conditionsToUpdate = state.conditions
            .filter(
                (condition: ContractTermCondition) =>
                    condition?.contractTermConditionId
            )
            .map((condition: ContractTermCondition) => ({
                conditions: updateConditions(
                    condition?.conditions,
                    countryCodes,
                    storeIds,
                    transactionTypeIds
                ),
                priority: condition?.priority,
                termRate: condition?.termRate,
                contractTermConditionId: condition?.contractTermConditionId,
            }));

        const conditionsToCreate = state.conditions
            .filter(
                (condition: ContractTermCondition) =>
                    !condition?.contractTermConditionId
            )
            .map((condition: ContractTermCondition) => ({
                conditions: updateConditions(
                    condition?.conditions,
                    countryCodes,
                    storeIds,
                    transactionTypeIds
                ),
                priority: condition?.priority,
                termRate: condition?.termRate,
            }));

        const updateVariables = {
            conditionsPayload: conditionsToUpdate,
            contractTermId,
        };

        const createVariables = {
            conditionsPayload: conditionsToCreate,
            contractTermId,
        };

        if (!isEmpty(conditionsToUpdate)) {
            updateContractTermConditions({ variables: updateVariables })
                .then(() => history.push(getContractDetail(contractId)))
                .catch(error => setErrors([error]));
        }

        if (!isEmpty(conditionsToCreate)) {
            createContractTermConditions({ variables: createVariables })
                .then(() => history.push(getContractDetail(contractId)))
                .catch(error => setErrors([error]));
        }
    };

    const deleteTermConditions = async () => {
        const { deletedConditions } = state;

        const existingConditionsToDelete = deletedConditions.map(
            (condition: ContractTermCondition) =>
                condition.contractTermConditionId
        );

        if (!isEmpty(existingConditionsToDelete) && termId) {
            await deleteContractTermConditions({
                variables: {
                    contractTermConditionId: existingConditionsToDelete,
                },
            }).catch(error => setErrors([error]));
        }

        return await Promise.resolve();
    };

    const saveHandler = () => {
        const validationErrors = contractTermFormValidation(state, true);
        if (validationErrors.length > 0) return setErrors(validationErrors);

        const successMessage =
            state.contractTermId === null
                ? `New ${termTypeCapitalized} Term has been successfully added.`
                : `${termTypeCapitalized} Term has been successfully updated.`;

        if (state.errors.length === 0) {
            setIsSaving(true);
            saveContractTerm()
                .then(
                    async contractTermId =>
                        await deleteTermConditions().then(() => {
                            toast(successMessage);
                            saveContractTermConditions(contractTermId);
                        })
                )
                .catch(error => {
                    setErrors([error]);
                    setIsSaving(false);
                });
        }
    };

    const deleteHandler = async () => {
        const deleteMessage = 'The term has been successfully deleted.';

        const deleteVariables = {
            contractTermId: termId,
        };

        await deleteContractTerm({ variables: deleteVariables })
            .then(() => {
                toast(deleteMessage);
            })
            .catch(error => {
                setErrors([error]);
                setIsModalOpen(false);
            });

        return await Promise.resolve();
    };

    const renderErrors = () => (
        <div>
            {errors.map((error, index) => (
                <Alert
                    className="error-alert"
                    key={`error-${index}`}
                    text={error}
                    variant="error"
                />
            ))}
        </div>
    );

    const renderAttachmentErrors = () => (
        <div>
            {attachmentErrors.map((attachmentError, index) => (
                <Alert
                    className="error-alert"
                    key={`error-${index}`}
                    text={attachmentError}
                    variant="error"
                />
            ))}
        </div>
    );

    if (!isValidTermType && errors.length) return renderErrors();

    return isFeatureCreateTermsWithNoTracksAndProducts &&
        termType !== 'label' ? (
        <ContractTermsDistroFormFields />
    ) : (
        <div className="ContractTermsDistro">
            <PageHeader breadcrumbs={breadcrumbs}>
                <>
                    <div style={{ display: 'flex', flexDirection: 'column' }}>
                        <SuitePageHeader.Title>
                            {pageTitle}
                        </SuitePageHeader.Title>
                        {termId && (
                            <span
                                className="suite-text-small"
                                style={{ padding: '2px 0' }}
                                data-testid="termId"
                            >
                                <b style={{ color: 'var(--text-secondary)' }}>
                                    ID:
                                </b>{' '}
                                {termId}
                            </span>
                        )}
                    </div>
                    {termId && (
                        <Button
                            className="ContractTermsDistro-Header-delete-btn ml-auto"
                            data-testid="deleteTermButton"
                            onClick={() => setIsModalOpen(true)}
                        >
                            Delete Term
                        </Button>
                    )}
                </>
            </PageHeader>
            <div className="ContractTermsDistro-Body">
                <ContractTermContext.Provider value={{ state, dispatch }}>
                    <ContractTermsDistroForm />
                </ContractTermContext.Provider>
                {errors.length > 0 && renderErrors()}
                {attachmentErrors.length > 0 && renderAttachmentErrors()}
                <div className="ContractTermsDistro-Body-Buttons">
                    <LinkButton
                        buttonSize="md"
                        label="Cancel"
                        target={getContractDetail(contractId)}
                        variant="default"
                    />
                    <Button
                        disabled={
                            !isEmpty(state.errors) ||
                            isSaving ||
                            !isEmpty(state.attachmentsValidError) ||
                            !isFormValid
                        }
                        onClick={saveHandler}
                        variant="primary"
                        data-testid="saveButton"
                    >
                        Save
                    </Button>
                </div>
            </div>
            <Modal
                isOpen={isModalOpen}
                onRequestClose={() => setIsModalOpen(false)}
                title="Delete Term"
                onConfirm={() => {
                    void deleteHandler().then(() => {
                        history.push(getContractDetail(contractId));
                    });
                }}
                confirmTitle="Confirm"
                testId="deleteModal"
            >
                Are you sure you want to delete this term?
                <div />
                This action cannot be undone.
            </Modal>
        </div>
    );
};

export default ContractTermsDistro;
