import React from 'react';
import {
    Alert,
    Card,
    Checkbox,
    LoadingIndicator,
    Button,
} from '@theorchard/suite-components';
import {
    formatMessage,
    Segment,
    useIdentity,
} from '@theorchard/suite-frontend';
import { GlyphIcon } from '@theorchard/suite-icons';
import { debounce } from 'lodash';
import { Link } from 'react-router-dom';
import { FEATURE_FLAGS } from 'src/constants';
import { useCreateGlobalSoundRecordingFromSpotify } from 'src/data/mutations';
import { useLazySoundRecordingSearch } from 'src/data/queries/soundRecordingSearch/soundRecordingSearch';
import { songUrl } from 'src/utils/urls';
import AssociatedSoundRecording from './associatedSoundRecording';
import type { PublishingGlobalSoundRecordingFromSpotifyMutation as SpotifyGSR } from 'src/data/mutations/createGlobalSoundRecordingFromSpotify/__generated__/createGlobalSoundRecordingFromSpotify';
import type { SoundRecordingType } from 'src/data/queries/soundRecordingSearch/soundRecordingSearch';
import type { AssociatedSoundRecording as AssociatedSoundRecordingType } from 'src/types';

// Workaround for React 18 + react-router-dom v5 type incompatibility
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const LinkAny = Link as any;

interface Props {
    associatedSounds: AssociatedSoundRecordingType[];
    setAssociatedSounds: React.Dispatch<
        React.SetStateAction<AssociatedSoundRecordingType[]>
    >;
    debounceTime: number;
    category: string;
    isrcDuplicateId?: string;
}

const toAssociatedSoundRecording = (
    item: SoundRecordingType
): AssociatedSoundRecordingType => ({
    id: item.id || '',
    isrc: item.isrc || '',
    name: item.name,
    performers: item.artists || [],
    writers: [],
    isLsr: item.isLsr,
});

const AssociatedSoundRecordings: React.FC<Props> = ({
    associatedSounds,
    setAssociatedSounds,
    debounceTime,
    category,
    isrcDuplicateId,
}) => {
    const identity = useIdentity();
    const isMultiSelectEnabled =
        identity.features[FEATURE_FLAGS.ADMIN_UX_IMPROVEMENTS];

    const [term, setTerm] = React.useState('');
    const [selectedIsrcs, setSelectedIsrcs] = React.useState<string[]>([]);

    const createGlobalSoundRecordingFromSpotify =
        useCreateGlobalSoundRecordingFromSpotify((data: SpotifyGSR) => {
            const gsrToAdd = {
                id: data.createGlobalSoundRecordingFromSpotify.id,
                isrc: data.createGlobalSoundRecordingFromSpotify.isrc,
                name: data.createGlobalSoundRecordingFromSpotify.name,
                performers:
                    data.createGlobalSoundRecordingFromSpotify.globalParticipants?.map(
                        g => g?.name || ''
                    ) || [],
                writers: [],
                isLsr: false,
                isSpotify: true,
            };
            // appended off the latest state: several Spotify recordings can be
            // created at once and each mutation completes on its own
            setAssociatedSounds(current => [...current, gsrToAdd]);
        });

    const [
        doSoundRecordingSearch,
        { soundRecordings, error, isLoading, isCalled },
    ] = useLazySoundRecordingSearch(associatedSounds, term);

    // eslint-disable-next-line react-hooks/exhaustive-deps
    const debounceSearch = React.useCallback(
        debounce((searchTerm: string) => {
            const variables = { term: searchTerm.toLowerCase() };
            doSoundRecordingSearch(variables);
        }, debounceTime),
        []
    );

    React.useEffect(() => {
        debounceSearch(term.trim());
        return () => debounceSearch.cancel();
    }, [term, debounceSearch]);

    const remove = (i: number) => {
        const copy = [...associatedSounds];
        copy.splice(i, 1);
        setAssociatedSounds(copy);
    };

    const addGsrs = (isrcs: string[]) => {
        const items = isrcs
            .map(isrc => soundRecordings.find(r => r.isrc === isrc))
            .filter((item): item is SoundRecordingType => !!item);

        items.forEach(() => {
            void Segment.trackEvent(
                'Click - Added Associated Sound Recording',
                {
                    category,
                }
            );
        });

        const existing = items.filter(item => !item.isSpotify);
        if (existing.length)
            setAssociatedSounds(current => [
                ...current,
                ...existing.map(toAssociatedSoundRecording),
            ]);

        items
            .filter(item => item.isSpotify)
            .forEach(item => {
                void createGlobalSoundRecordingFromSpotify({
                    variables: { id: item.id },
                });
            });
    };

    const addGsr = (isrc: string) => {
        addGsrs([isrc]);
        setTerm('');
    };

    const toggleIsrc = (isrc: string) =>
        setSelectedIsrcs(current =>
            current.includes(isrc)
                ? current.filter(selected => selected !== isrc)
                : [...current, isrc]
        );

    const resultIsrcs = soundRecordings
        .map(r => r.isrc)
        .filter((isrc): isrc is string => !!isrc);
    // a selection only means anything against the results on screen, so stale
    // ISRCs are dropped rather than reset through an effect on the search term
    const selected = selectedIsrcs.filter(isrc => resultIsrcs.includes(isrc));
    const selectedCount = selected.length;
    const allSelected =
        resultIsrcs.length > 0 && selectedCount === resultIsrcs.length;

    const toggleAll = () =>
        setSelectedIsrcs(allSelected ? [] : [...resultIsrcs]);

    // the search term is kept so more recordings can be added from the results
    const addSelected = () => {
        addGsrs(selected);
        setSelectedIsrcs([]);
    };

    let errorMessage = '';
    if (error) {
        if (error.spotifyError)
            errorMessage = formatMessage(
                'newSong.errorFetchingSongsFromSpotify'
            );

        if (error.owsError || error.gsrError)
            errorMessage = formatMessage('newSong.errorFetchingSongs');
    }
    return (
        <Card suite className="NewSong-sound-recordings">
            <div className="NewSong-header">
                <div className="NewSong-header-title">
                    {formatMessage('newSong.associatedSoundRecording')}
                </div>
                <div className="NewSong-header-subtitle">
                    {formatMessage('newSong.associatedSoundRecordingSubTitle')}
                </div>
            </div>

            {associatedSounds.map((associatedSound, i) => (
                <AssociatedSoundRecording
                    associatedSound={associatedSound}
                    remove={() => remove(i)}
                    key={associatedSound.id}
                />
            ))}

            {!!isrcDuplicateId && (
                <Alert
                    variant="warn"
                    text={
                        <div className="d-flex align-items-center justify-content-between">
                            <span>
                                {formatMessage('newSong.sameIsrcSongExists')}
                            </span>
                            <LinkAny to={songUrl(isrcDuplicateId)}>
                                <Button variant="link" size="sm">
                                    {formatMessage('newSong.goToSong')}
                                    <GlyphIcon name="arrowRight" size={12} />
                                </Button>
                            </LinkAny>
                        </div>
                    }
                />
            )}

            <div className="NewSong-sound-recording-search">
                <div className="NewSong-sound-recording-search-title">
                    {formatMessage('newSong.searchForSound')}
                </div>
                <input
                    type="text"
                    className="form-control"
                    value={term}
                    placeholder={formatMessage(
                        'newSong.searchForSoundPlaceholder'
                    )}
                    onChange={e => setTerm(e.target.value)}
                />

                {term.length > 0 && (
                    <>
                        {isLoading && <LoadingIndicator />}
                        {soundRecordings.length === 0 &&
                            isCalled &&
                            !isLoading && (
                                <div className="NewSong-sound-recordings-search-no-results">
                                    {formatMessage(
                                        'selectExistingSong.noResults'
                                    )}
                                </div>
                            )}
                        {errorMessage.length > 0 && !isLoading && isCalled && (
                            <div className="NewSong-header-subtitle">
                                {errorMessage}
                            </div>
                        )}
                        {isMultiSelectEnabled &&
                            !isLoading &&
                            soundRecordings.length > 0 && (
                                <div className="NewSong-sound-recordings-search-actions d-flex align-items-center justify-content-between px-3 py-2">
                                    <Checkbox
                                        id="sound-recordings-select-all"
                                        label={formatMessage(
                                            'newSong.selectAllSoundRecordings'
                                        )}
                                        checked={allSelected}
                                        indeterminate={
                                            selectedCount > 0 && !allSelected
                                        }
                                        onChange={toggleAll}
                                    />
                                    <Button
                                        variant="primary"
                                        size="sm"
                                        disabled={selectedCount === 0}
                                        onClick={addSelected}
                                    >
                                        {formatMessage(
                                            'newSong.addSelectedSoundRecordings',
                                            { count: selectedCount }
                                        )}
                                    </Button>
                                </div>
                            )}
                        <div className="NewSong-sound-recordings-search-results">
                            {!isLoading &&
                                soundRecordings.map(r => (
                                    <div
                                        key={r.isrc}
                                        className="NewSong-sound-recordings-search-row"
                                        onClick={() =>
                                            r.isrc &&
                                            (isMultiSelectEnabled
                                                ? toggleIsrc(r.isrc)
                                                : addGsr(r.isrc))
                                        }
                                    >
                                        <div className="NewSong-sound-recordings-search-row-left">
                                            {isMultiSelectEnabled && (
                                                <Checkbox
                                                    className="mr-2"
                                                    id={`sound-recording-${r.isrc}`}
                                                    checked={selected.includes(
                                                        r.isrc || ''
                                                    )}
                                                    onClick={e =>
                                                        e.stopPropagation()
                                                    }
                                                    onChange={() =>
                                                        r.isrc &&
                                                        toggleIsrc(r.isrc)
                                                    }
                                                />
                                            )}
                                            {r?.artworkUrl ? (
                                                <img
                                                    alt="album artwork"
                                                    src={r.artworkUrl}
                                                    className="NewSong-sound-recordings-search-image"
                                                />
                                            ) : (
                                                <div className="NewSong-sound-recordings-search-image-placeholder" />
                                            )}
                                            <div>
                                                <div>{r?.name}</div>
                                                <div>
                                                    {r?.artists.join(', ')}
                                                </div>
                                            </div>
                                        </div>
                                        <div>{r?.isrc}</div>
                                    </div>
                                ))}
                        </div>
                    </>
                )}
            </div>
        </Card>
    );
};

export default AssociatedSoundRecordings;
