import React, { useCallback } from 'react';
import { useApolloClient } from '@apollo/client';
import {
    HelpTooltip,
    Field,
    SearchDropdown,
    TruncatedText,
} from '@theorchard/suite-components';
import { formatMessage } from '@theorchard/suite-frontend';
import { GlyphIcon } from '@theorchard/suite-icons';
import { defaultSongWriterValues } from 'src/components/songWriterAsyncSelect/songWriterAsyncSelect';
import {
    usePublishingSongWriters,
    getPromisedSongWriterSearch,
} from 'src/data/queries';
import type { PublishingSongWritersFilterLabelInput } from 'src/data/queries';
import type {
    Label,
    SongWriter,
    SelectOptionComponent,
    SongWriterState,
    DropdownOption,
} from 'src/types';

const CLASSNAME = 'SongWriterSelector';

const songWriterToAgreements = (sw: SongWriter) => {
    const songWritersState: SongWriterState[] =
        sw.agreements?.map(agreement => {
            const { publisher } = agreement;

            return {
                id: sw.id,
                agreementId: agreement.id,
                controlled: agreement.controlled,
                pro: sw.pro || '',
                ipi: sw.ipi || '',
                legalName: sw.legalName,
                publisherName: publisher?.name ?? '',
                professionallyKnownAs: sw.professionallyKnownAs || [],
            };
        }) || [];

    return songWritersState;
};

const SongWriterOption = (props: SelectOptionComponent<SongWriter>) => {
    const swData: SongWriterState = props.data! as unknown as SongWriterState;
    const { id, legalName, publisherName, controlled } = swData;

    const controlledText = controlled ? 'Controlled' : 'Non-Controlled';
    const publisherText = publisherName ? ` • ${publisherName}` : '';
    const fullConcatenatedText = `${legalName}${publisherText} • ${controlledText}`;

    const addNewOption = (
        <div className="d-flex flex-column">
            <span className={`${CLASSNAME}-addNewSongwriter`}>
                <GlyphIcon name="plus" size={12} />
                {legalName}
            </span>
        </div>
    );

    const optionWithGreyedControlled = (
        <div className="d-flex flex-column">
            <span>{legalName}</span>
            <span className="text-muted">{`${controlledText}${publisherText}`}</span>
        </div>
    );

    if (props.component === 'menu')
        return (
            <div>
                {id === 'addNewSongwriter'
                    ? addNewOption
                    : optionWithGreyedControlled}
            </div>
        );

    return (
        <div>
            <HelpTooltip
                id={CLASSNAME}
                tooltipPlacement="top"
                message={fullConcatenatedText}
            >
                {id === 'addNewSongwriter' ? (
                    addNewOption
                ) : (
                    <div className="ellipsis">{fullConcatenatedText}</div>
                )}
            </HelpTooltip>
        </div>
    );
};

const CustomSongWriterOption = (props: SelectOptionComponent<SongWriter>) => {
    const swData: SongWriterState = props.data! as unknown as SongWriterState;
    const { legalName, professionallyKnownAs } = swData;

    return (
        <div>
            <div className="d-flex flex-column">
                <span>{legalName}</span>
                {professionallyKnownAs && (
                    <TruncatedText
                        testId={`${CLASSNAME}-pka-HelpTooltip`}
                        className="text-muted"
                        text={professionallyKnownAs.join(', ')}
                        maxWidth={500}
                    />
                )}
            </div>
        </div>
    );
};

interface ExcludeFilter {
    agreementIds: string[];
}

interface Props {
    withAddNew?: boolean;
    isClearable?: boolean;
    isMulti?: boolean;
    label?: Label | null;
    defaultValue: Partial<SongWriter>[] | null;
    onChange: (songWriters: SongWriter[] | null) => void;
    excludeFilter?: ExcludeFilter;
    unwrap?: boolean;
}

const generateDropDownValue = (
    swList: Partial<SongWriter>[]
): DropdownOption<SongWriter>[] =>
    swList.map(sw => {
        return {
            label: sw.legalName,
            value: sw.id || sw.legalName || '',
            data: {
                ...defaultSongWriterValues,
                ...sw,
            },
        };
    });

const generateDropDownOptions = (
    swList: (
        | Partial<SongWriter>[]
        | SongWriterState
        | { id: string; legalName: string }
    )[] = []
): DropdownOption<SongWriter>[] => {
    const result: DropdownOption<SongWriter>[] = [];

    const castToSongWriter = (
        sws:
            | Partial<SongWriter>
            | SongWriterState
            | { id: string; legalName: string }
    ): SongWriter => ({
        ...defaultSongWriterValues,
        ...sws,
    });

    swList.forEach(sw => {
        if ('legalName' in sw) {
            result.push({
                label: sw.legalName,
                value: sw.id,
                data: castToSongWriter(sw),
            });
        }

        if (Array.isArray(sw))
            result.push(
                ...sw.map(swi => {
                    return {
                        label: swi.legalName || '',
                        value: swi.id || '',
                        data: castToSongWriter(swi),
                    };
                })
            );
    });

    return result;
};

const SongWriterSelector: React.FC<Props> = ({
    withAddNew = false,
    isClearable,
    isMulti = false,
    label,
    defaultValue,
    onChange,
    excludeFilter,
    unwrap,
}) => {
    const client = useApolloClient();

    const { data } = usePublishingSongWriters({ label });
    const songWriters: SongWriterState[] = [];
    data?.songWriters?.forEach(sw => {
        songWriters.push(...songWriterToAgreements(sw));
    });

    const loadSongWritersOptions = async (
        term: string
    ): Promise<DropdownOption<SongWriter>[]> => {
        const filter: PublishingSongWritersFilterLabelInput = {
            legalNameSearch: term,
            label,
            professionallyKnownAs: term,
        };

        const sws = await getPromisedSongWriterSearch(filter, client);

        const songWritersPromisedData: SongWriterState[] = [];
        sws?.forEach(sw => {
            songWritersPromisedData.push(
                ...songWriterToAgreements(sw).filter(
                    s => !excludeFilter?.agreementIds.includes(s.agreementId)
                )
            );
        });

        return generateDropDownOptions(songWritersPromisedData);
    };

    const defaultOptions = [
        ...(withAddNew
            ? [
                  {
                      id: 'addNewSongwriter',
                      legalName: formatMessage(`${CLASSNAME}.addNewSongwriter`),
                  },
              ]
            : []),
        ...(isMulti &&
        defaultValue &&
        !songWriters?.some(
            sw =>
                sw.id === defaultValue[0]?.id ||
                sw.legalName === defaultValue[0]?.legalName
        )
            ? [defaultValue]
            : []),
        ...(songWriters || []),
    ].filter(
        sw =>
            !('agreementId' in sw) ||
            !excludeFilter?.agreementIds.includes(sw.agreementId)
    );

    const onSongWriterSelect = useCallback(
        // eslint-disable-next-line
        // @ts-ignore
        value => {
            if (!value) onChange(null);
            else
                onChange(
                    Array.isArray(value) ? value.map(v => v.data) : [value.data]
                );
        },
        [onChange]
    );

    const renderDropDown = () => (
        <SearchDropdown
            isMulti={isMulti}
            className={CLASSNAME}
            isClearable={isClearable}
            value={generateDropDownValue(defaultValue || [])}
            defaultValue={generateDropDownValue(defaultValue || [])}
            defaultOptions={generateDropDownOptions(defaultOptions)}
            onLoadOptions={loadSongWritersOptions}
            onChange={onSongWriterSelect}
            placeholder={formatMessage(`${CLASSNAME}.songWritersPlaceholder`)}
            {...(!isMulti
                ? {
                      formatOptionLabel: (option, meta) => (
                          <SongWriterOption
                              {...option}
                              component={meta.context}
                          />
                      ),
                  }
                : {
                      formatOptionLabel: (option, meta) => (
                          <CustomSongWriterOption
                              {...option}
                              component={meta.context}
                          />
                      ),
                  })}
        />
    );

    if (unwrap) return renderDropDown();

    return (
        <Field
            errorMessage={
                defaultValue === null
                    ? formatMessage('errors.required')
                    : undefined
            }
            controlId="songWriter"
            labelText=""
        >
            {renderDropDown()}
        </Field>
    );
};

export default SongWriterSelector;
