import React, { useCallback } from 'react';
import { useApolloClient } from '@apollo/client';
import { Field, SearchDropdown } from '@theorchard/suite-components';
import SongWriterOption from 'src/components/songWriterSelector/songWriterOption';
import {
    usePublishingSongWriters,
    getPromisedSongWriterSearch,
} from 'src/data/queries/songWritersSearch/songWritersSearch';
import type { PublishingSongWritersFilterLabelInput } from 'src/data/queries/songWritersSearch/songWritersSearch';
import type {
    Label,
    SongWriter,
    SongWriterState,
    DropdownOption,
} from 'src/types';

const CLASSNAME = 'SongWriterSelector';
const INTL_CLASSNAME = 'publishing.songWriterSelector';

const defaultSongWriterValues: SongWriter = {
    id: '',
    legalName: '',
    vendor: {
        id: { vendorId: 0, subaccountId: 0 },
        uuid: '',
        name: '',
    },
    ipi: null,
    pro: null,
    compositions: [],
    agreements: [],
};

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 ?? '',
            };
        }) || [];

    return songWritersState;
};

interface ExcludeFilter {
    agreementIds: string[];
}

export 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 => ({
        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 => ({
                    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,
        };

        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: $t(
                          `${INTL_CLASSNAME}.placeholders.addNewSongwriter`
                      ),
                  },
              ]
            : []),
        ...(isMulti &&
        defaultValue &&
        !songWriters?.some(sw => sw.id === defaultValue[0]?.id)
            ? [defaultValue]
            : []),
        ...(songWriters || []),
    ].filter(
        sw =>
            !('agreementId' in sw) ||
            !excludeFilter?.agreementIds.includes(sw.agreementId)
    );

    const onSongWriterSelect = useCallback(
        // eslint-disable-next-line
        // @ts-expect-error
        value => {
            if (!value) onChange(null);
            else
                onChange(
                    Array.isArray(value)
                        ? value.map((v: DropdownOption<SongWriter>) => 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={$t(`${INTL_CLASSNAME}.placeholders.songwriters`)}
            {...(!isMulti
                ? {
                      formatOptionLabel: (option, meta) => (
                          <SongWriterOption
                              {...option}
                              component={meta.context}
                          />
                      ),
                  }
                : {})}
        />
    );

    if (unwrap) return renderDropDown();

    return (
        <Field
            errorMessage={
                defaultValue === null ? $t('error.requiredField') : undefined
            }
            controlId="songWriter"
            labelText=""
        >
            {renderDropDown()}
        </Field>
    );
};

export default SongWriterSelector;
