import type { FC } from 'react';
import React, { useCallback, useEffect, useMemo } from 'react';
import { useApolloClient } from '@apollo/client';
import { SearchDropdown, Field } from '@theorchard/suite-components';
import { useOrchardLabelByIdQuery } from 'src/data/queries/orchardLabel/orchardLabelById';
import { useOrchardLabels } from 'src/data/queries/orchardLabels/orchardLabels';
import { getPromisedLabelSearch } from 'src/data/queries/orchardLabelSearch/orchardLabelSearch';
import type { DropdownOption, SearchableDropdownOption } from 'src/types';
import type { Label } from 'src/types';

const CLASSNAME = 'LabelSearchDropdown';
export const INTL_CLASSNAME = 'publishing.labelSearchDropdown';

export interface Props {
    unwrap?: boolean;
    disabled?: boolean;
    onChange: (data?: Label) => void;
    vendorId?: number;
    formatOptionLabel?: (option: DropdownOption<Label>) => JSX.Element;
    errorMessage?: string;
}

export const getOptionName = (label: Label) =>
    `${label.name} - ${label.uuid} - ${label.id.vendorId}`;

const createOption = (label: Label): DropdownOption<Label> => ({
    label: getOptionName(label),
    value: label.uuid,
    data: label,
});

const LabelSearchDropdown: FC<Props> = ({
    onChange,
    unwrap,
    disabled,
    vendorId,
    formatOptionLabel,
    errorMessage,
}) => {
    const apolloClient = useApolloClient();
    const { data: listData } = useOrchardLabels();
    // our vendors won't have subaccountIds so can just set this to 0.
    const variables = { id: { subaccountId: 0, vendorId } };
    const { data: selectedLabel } = useOrchardLabelByIdQuery(variables);

    const handleInputChange = async (searchTerm: string) => {
        const data = await getPromisedLabelSearch(searchTerm, apolloClient);

        return data.map(createOption);
    };

    const labelOptions = useMemo<DropdownOption<Label>[]>(
        () => [...(listData ?? [])].map(createOption),
        [listData]
    );

    const handleChange = useCallback(
        (newValue?: SearchableDropdownOption<Label>) => {
            onChange(newValue?.data);
        },
        [onChange]
    );

    const handleSingleLabel = useCallback(
        (newValue?: Label) => {
            onChange(newValue);
        },
        [onChange]
    );

    useEffect(() => {
        if (listData?.length === 1 && listData[0].id.vendorId !== vendorId)
            handleSingleLabel(listData[0]);
    }, [vendorId, listData, handleSingleLabel]);

    let selectedOption: DropdownOption<Label> | null = null;
    if (selectedLabel?.name)
        selectedOption = createOption({
            id: {
                vendorId: selectedLabel.id.vendorId,
                subaccountId: 0,
            },
            name: selectedLabel.name,
            uuid: selectedLabel.uuid,
        });

    const formattedOption = (option: DropdownOption<Label>) => (
        <div className={`${CLASSNAME}-option`}>
            <div className={`${CLASSNAME}-option-name`}>
                {option.data?.name}
            </div>
            <div className={`${CLASSNAME}-option-id`}>
                {option.data?.id.vendorId}
            </div>
        </div>
    );

    const renderDropDown = () => (
        <SearchDropdown
            name="labelSearchDropdown"
            className={CLASSNAME}
            onChange={handleChange}
            onLoadOptions={handleInputChange}
            defaultOptions={labelOptions}
            isDisabled={disabled}
            value={selectedOption}
            formatOptionLabel={formatOptionLabel || formattedOption}
            placeholder={$t(`${INTL_CLASSNAME}.placeholders.searchForLabel`)}
            debounceTime={300}
        />
    );

    if (listData?.length === 1) return <div className={`${CLASSNAME} hide`} />;

    if (unwrap) return renderDropDown();

    return (
        <Field
            controlId="labelAsyncSelect"
            labelText={$t('common.fieldNames.labelAccount')}
            errorMessage={errorMessage}
        >
            {renderDropDown()}
        </Field>
    );
};

export default LabelSearchDropdown;
