import React, { useEffect, useRef } from 'react';
import { MultiSelect } from '@theorchard/suite-components';
import type { TemplateLookupResult } from 'src/types';
import {
    getSpotifyUrlType,
    isValidSpotifyId,
    type SpotifyResourceType,
} from './getSpotifyUrlType';

const FILTER_INPUT_SELECTOR = '[data-testid="SuiteListViewFilterInput"]';

const SpotifySelect = ({
    lookup,
    onChange,
    expectedType,
    invalidTypeMessage,
    invalidLinkMessage,
}: {
    lookup: (query: string) => Promise<TemplateLookupResult | null>;
    onChange: (value: TemplateLookupResult[]) => void;
    expectedType: 'artist' | 'album';
    invalidTypeMessage: (pastedType: SpotifyResourceType) => string;
    invalidLinkMessage: () => string;
}) => {
    const rawInputRef = useRef('');
    const isOpenRef = useRef(false);

    /**
     * The <MultiSelect/> component always forces the value passed to onLoadOptions to
     * be lowercase, but Spotify URLs are case-sensitive. The <MultiSelect/> component
     * doesn't provide any way to override this behavior, and its dropdown renders
     * in a portal outside our component tree, so we listen on document in the
     * capture phase to grab the raw input value before MultiSelect lowercases it.
     *
     * Every SpotifySelect on the page shares this document listener and the filter
     * inputs all carry the same testid, so we only record while THIS dropdown is
     * open. Otherwise a value typed into one field would also land in the others'
     * refs and get looked up there — only the field you're typing in should search.
     */
    useEffect(() => {
        const handler = (e: Event) => {
            if (!isOpenRef.current) return;
            const target = e.target as HTMLInputElement;
            if (target.matches?.(FILTER_INPUT_SELECTOR)) {
                rawInputRef.current = target.value;
            }
        };
        document.addEventListener('input', handler, true);
        return () => document.removeEventListener('input', handler, true);
    }, []);

    return (
        <MultiSelect
            onOpen={() => {
                isOpenRef.current = true;
                rawInputRef.current = '';
            }}
            onClose={() => {
                isOpenRef.current = false;
                rawInputRef.current = '';
            }}
            onLoadOptions={async multiSelectTerm => {
                const rawTerm = rawInputRef.current.trim();
                const termForValidation =
                    rawTerm || multiSelectTerm?.trim() || '';
                if (!termForValidation) return { data: [] };
                const detected = getSpotifyUrlType(termForValidation);
                if (detected && detected !== expectedType) {
                    throw new Error(invalidTypeMessage(detected));
                }
                if (!detected && !isValidSpotifyId(termForValidation)) {
                    throw new Error(invalidLinkMessage());
                }
                const termForLookup = rawTerm || termForValidation;
                const result = await lookup(termForLookup);
                return {
                    data: result
                        ? [{ label: result.name, value: result.url }]
                        : [],
                };
            }}
            onChange={(value: { label: string; value: string }[]) =>
                onChange(
                    value.map(artist => ({
                        name: artist.label,
                        url: artist.value,
                    }))
                )
            }
        />
    );
};

export default SpotifySelect;
