import React, { useEffect, useRef, useState } from 'react';
import { MultiSelect } from '@theorchard/suite-components';
import { isEmpty, isEqual } from 'lodash-es';
import { useContractTermsByAccount } from 'src/apollo/queries/contract-term';
import {
    useProductsByUpcs,
    useProductSearch,
} from 'src/apollo/queries/product-search';
import {
    CONTRACT_TERM_TYPES,
    PRODUCT_ALREADY_ATTACHED_TO_ACCOUNT_ERROR,
    PRODUCT_ALREADY_ATTACHED_TO_CONTRACT_ERROR,
} from 'src/constants';
import { ValidationErrorType } from 'src/types/validation';
import {
    getNewAttachmentsWithinContractAndTerm,
    getContractTermAttachments,
    getOtherAttachmentsWithinContract,
    validateSelectedAttachmentsWithinAccount,
    validateSelectedAttachmentsWithinContract,
} from 'src/utils/contract-validation';
import type { GetContractWithAttachmentsQuery } from 'src/apollo/queries/contract/__generated__/get-contract-attachments';
import type { GetProductsByUpcsQuery } from 'src/apollo/queries/product-search/__generated__/get-products-by-upcs';
import type { SelectedProduct } from 'src/types/abacus-contract';
import type { ValidationError } from 'src/types/validation';

type ContractTerms = NonNullable<
    NonNullable<
        GetContractWithAttachmentsQuery['abacusContract']
    >['contractTerms']
>[0];

type Products = GetProductsByUpcsQuery;
type Product = Products['productByUpcs'][0];

const SEARCH_PLACEHOLDER = 'Search for product(s)';

interface ProductMultiSelectPropTypes {
    accountId: string;
    contractTermId: string;
    contractTerms: ContractTerms[];
    onChange: (params: any) => void;
    productUpcs: string[];
    labelIds: any;
    setErrors: any;
}

const ProductMultiSelect: React.FC<ProductMultiSelectPropTypes> = ({
    accountId,
    contractTermId,
    contractTerms,
    onChange,
    productUpcs,
    labelIds,
    setErrors,
}) => {
    const selectName = 'productUpcs';
    const { data: accountContractTerms, getContractsTerms } =
        useContractTermsByAccount();
    const [selectedProductsWithinTerm, setSelectedProductsWithinTerm] =
        useState<SelectedProduct[]>([]);
    const [selectedProductsUpcsWithinTerm, setSelectedProductsUpcsWithinTerm] =
        useState<string[]>([]);

    const { data: upcsData, getProductsByUpcs } = useProductsByUpcs();

    const prevUpcsRef = useRef<string[]>([]);

    const originalAttachmentsWithinTerm = getContractTermAttachments(
        contractTerms,
        contractTermId
    );
    const otherAttachmentsWithinContract = getOtherAttachmentsWithinContract(
        contractTerms,
        contractTermId,
        CONTRACT_TERM_TYPES.PRODUCT
    );

    const validateAttachments = () => {
        const errorType = ValidationErrorType.UNIQUENESS;
        const fieldName = 'productUpcs';
        const newAttachmentsWithinContractAndTerm =
            getNewAttachmentsWithinContractAndTerm(
                selectedProductsUpcsWithinTerm,
                originalAttachmentsWithinTerm,
                otherAttachmentsWithinContract
            );

        const attachmentValidationErrors: ValidationError[] = [];
        const contractValidationErrors =
            validateSelectedAttachmentsWithinContract(
                selectedProductsUpcsWithinTerm,
                otherAttachmentsWithinContract,
                errorType,
                fieldName,
                PRODUCT_ALREADY_ATTACHED_TO_CONTRACT_ERROR
            );
        const allContractTermsForAccount =
            accountContractTerms?.abacusContractTermsByAccount;
        const allAttachmentsWithinAccountFlattened =
            allContractTermsForAccount?.flatMap(
                term => term.attachments || []
            ) || [];
        const accountValidationErrors =
            validateSelectedAttachmentsWithinAccount(
                allAttachmentsWithinAccountFlattened,
                newAttachmentsWithinContractAndTerm,
                errorType,
                fieldName,
                PRODUCT_ALREADY_ATTACHED_TO_ACCOUNT_ERROR
            );
        attachmentValidationErrors.push(
            ...contractValidationErrors,
            ...accountValidationErrors
        );

        return attachmentValidationErrors;
    };

    useEffect(() => {
        const prev = prevUpcsRef.current;
        if (!isEqual(prev, productUpcs)) {
            prevUpcsRef.current = productUpcs;

            if (Array.isArray(productUpcs) && productUpcs.length > 0) {
                getProductsByUpcs(productUpcs);
            } else {
                setSelectedProductsWithinTerm([]);
                setSelectedProductsUpcsWithinTerm([]);
            }
        }
    }, [productUpcs, getProductsByUpcs]);

    useEffect(() => {
        if (upcsData) {
            const arr: any[] = (upcsData as Products).productByUpcs || [];
            if (Array.isArray(arr)) {
                const items: SelectedProduct[] = arr
                    .filter(prod => prod && prod.upc)
                    .map((prod: Product) => {
                        const name = prod.productName ?? 'Unknown';
                        const upc = prod.upc ?? 'unknown';
                        return {
                            label: `${name}-${upc}`,
                            value: upc,
                        };
                    });
                setSelectedProductsWithinTerm(items);
                setSelectedProductsUpcsWithinTerm(items.map(i => i.value));
            } else {
                setSelectedProductsWithinTerm([]);
                setSelectedProductsUpcsWithinTerm([]);
            }
        }
    }, [upcsData]);

    useEffect(() => {
        setErrors(validateAttachments());
    }, [accountContractTerms, selectedProductsWithinTerm]);

    const getPromisedProductSearch = useProductSearch();
    const performSearch = async (term?: string) => {
        if (!term) return {};
        const trimmedTerm = term.trim().replace('\\', '');
        if (trimmedTerm && !isEmpty(labelIds)) {
            const data = await getPromisedProductSearch(
                trimmedTerm,
                labelIds,
                100,
                0
            );
            const searchResults =
                data?.map((row: any) => ({
                    label: `${row.productName}-${row.upc}`,
                    value: row.upc!,
                })) || [];
            return { data: searchResults };
        }
        return {};
    };

    const onItemChange = (items: SelectedProduct[]) => {
        const upcValues = items.map(i => i.value);
        onChange({
            target: {
                name: selectName,
                value: upcValues,
            },
        });
        setSelectedProductsWithinTerm(items);
        setSelectedProductsUpcsWithinTerm(upcValues);
    };

    const onClose = () => {
        const newAttachmentsWithinContractAndTerm =
            getNewAttachmentsWithinContractAndTerm(
                selectedProductsUpcsWithinTerm,
                originalAttachmentsWithinTerm,
                otherAttachmentsWithinContract
            );
        if (!isEmpty(newAttachmentsWithinContractAndTerm)) {
            getContractsTerms(
                accountId,
                newAttachmentsWithinContractAndTerm,
                CONTRACT_TERM_TYPES.PRODUCT.toLowerCase()
            );
        }
    };

    return (
        <MultiSelect
            className="product-select"
            controlId="productSearch"
            id="productSearch"
            onChange={onItemChange}
            onClose={onClose}
            onLoadOptions={performSearch}
            placeholder={SEARCH_PLACEHOLDER}
            selectedValue={selectedProductsWithinTerm}
            disabled={isEmpty(labelIds)}
            menuMinWidth="100%"
        />
    );
};

export default ProductMultiSelect;
