import React, { useEffect, useState } from 'react';
import { MultiSelect } from '@theorchard/suite-components';
import { useFeatureFlag } from '@theorchard/suite-frontend';
import { isEmpty, uniqBy } from 'lodash-es';
import {
    useContractTermsByAccount,
    useContractTermAttachments,
} from 'src/apollo/queries/contract-term';
import {
    useTrackByIsrc,
    useTrackSearch,
    useTrackSearchByLabelIds,
} from 'src/apollo/queries/track-search';
import {
    CONTRACT_TERM_TYPES,
    TRACKS_ALREADY_ATTACHED_TO_ACCOUNT_ERROR,
    TRACKS_ALREADY_ATTACHED_TO_CONTRACT_ERROR,
    USER_FEATURES,
} from 'src/constants';
import { ValidationErrorType } from 'src/types/validation';
import {
    getNewAttachmentsWithinContractAndTerm,
    getContractTermAttachments,
    getOtherAttachmentsWithinContract,
    validateSelectedAttachmentsWithinAccount,
    validateSelectedAttachmentsWithinContract,
} from 'src/utils/contract-validation';
import type { GetContractWithAttachmentsQuery } from 'src/apollo/queries/contract/__generated__/get-contract-attachments';
import type { GetTracksByLabelIdQuery } from 'src/apollo/queries/track-search/__generated__/get-tracks-by-label-id';
import type { GetTracksByLabelIdsQuery } from 'src/apollo/queries/track-search/__generated__/get-tracks-by-label-ids';
import type { SelectedTrack } from 'src/types/abacus-contract';
import type { ValidationError } from 'src/types/validation';

type GlobalSoundRecordingItem =
    GetTracksByLabelIdQuery['globalSoundRecordingSearchES']['items'][0];
type ContractTerms = NonNullable<
    NonNullable<
        GetContractWithAttachmentsQuery['abacusContract']
    >['contractTerms']
>[0];

type TrackByLabelId = GetTracksByLabelIdsQuery['trackSearch']['tracks'][0];

const SEARCH_PLACEHOLDER = 'Search for track(s)';

export interface TrackMultiSelectPropTypes {
    accountId: string;
    contractTermId: string;
    contractTerms: ContractTerms[];
    onChange: (params: any) => void;
    tracksIsrcs: string[];
    labelIds: any;
    setErrors: any;
}

const TrackMultiSelect: React.FC<TrackMultiSelectPropTypes> = ({
    accountId,
    contractTermId,
    contractTerms,
    onChange,
    tracksIsrcs,
    labelIds,
    setErrors,
}) => {
    const selectName = 'trackIsrcs';
    const { data: accountContractTerms, getContractsTerms } =
        useContractTermsByAccount();
    const [selectedTracksWithinTerm, setSelectedTracksWithinTerm] = useState<
        SelectedTrack[]
    >([]);
    const [selectedTracksIsrcsWithinTerm, setSelectedTracksIsrcsWithinTerm] =
        useState<string[]>([]);
    const [selectedTracksIsrcs, setSelectedTracksIsrcs] = useState<string[]>(
        []
    );
    const [tracksData, setTracksData] = useState<any>([]);
    const [requestParams, setRequestParams] = useState({
        limit: 5,
        offset: 0,
        totalPages: 1,
        currentPageCount: 1,
    });

    const { data: trackData, getTrackByIsrc } = useTrackByIsrc();
    const getContractsTermsAttachments = useContractTermAttachments();
    const [areTracksLoaded, setAreTracksLoaded] = useState(false);

    const isFeatureAbacusImproveTrackSearchEnabled = useFeatureFlag(
        USER_FEATURES.ABACUS_IMPROVE_TRACK_SEARCH
    );

    const getPromisedTrackSearch = useTrackSearch();
    const getTrackSearch = useTrackSearchByLabelIds();

    const originalAttachmentsWithinTerm = getContractTermAttachments(
        contractTerms,
        contractTermId
    );
    const otherAttachmentsWithinContract = getOtherAttachmentsWithinContract(
        contractTerms,
        contractTermId,
        CONTRACT_TERM_TYPES.TRACK
    );

    const validateAttachments = () => {
        const errorType = ValidationErrorType.UNIQUENESS;
        const fieldName = 'trackIsrcs';
        const newAttachmentsWithinContractAndTerm =
            getNewAttachmentsWithinContractAndTerm(
                isFeatureAbacusImproveTrackSearchEnabled
                    ? selectedTracksIsrcs
                    : selectedTracksIsrcsWithinTerm,
                originalAttachmentsWithinTerm,
                otherAttachmentsWithinContract
            );

        const attachmentValidationErrors: ValidationError[] = [];
        const contractValidationErrors =
            validateSelectedAttachmentsWithinContract(
                isFeatureAbacusImproveTrackSearchEnabled
                    ? selectedTracksIsrcs
                    : selectedTracksIsrcsWithinTerm,
                otherAttachmentsWithinContract,
                errorType,
                fieldName,
                TRACKS_ALREADY_ATTACHED_TO_CONTRACT_ERROR
            );
        const allContractTermsForAccount =
            accountContractTerms?.abacusContractTermsByAccount;
        const allAttachmentsWithinAccountFlattened =
            allContractTermsForAccount?.flatMap(
                term => term.attachments || []
            ) || [];

        const accountValidationErrors =
            validateSelectedAttachmentsWithinAccount(
                allAttachmentsWithinAccountFlattened,
                newAttachmentsWithinContractAndTerm,
                errorType,
                fieldName,
                TRACKS_ALREADY_ATTACHED_TO_ACCOUNT_ERROR
            );
        attachmentValidationErrors.push(
            ...contractValidationErrors,
            ...accountValidationErrors
        );

        return attachmentValidationErrors;
    };

    const performSearch = async (term?: string) => {
        if (!term) return {};

        const trimmedTerm = term.trim().replace('\\', '');
        if (trimmedTerm && !isEmpty(labelIds)) {
            if (isFeatureAbacusImproveTrackSearchEnabled) {
                const searchResults: any = [];
                const updatedFilterIds = labelIds.map((labelId: string) =>
                    parseInt(labelId, 10)
                );
                const data = await getTrackSearch(
                    trimmedTerm,
                    updatedFilterIds,
                    25,
                    0
                );
                data?.forEach((row: TrackByLabelId) => {
                    if (row?.isrc && !tracksIsrcs.includes(row?.isrc)) {
                        searchResults.push({
                            label: `${row.name}-${row.isrc}`,
                            value: row.isrc,
                        });
                    }
                });
                if (!isEmpty(searchResults))
                    return { data: uniqBy(searchResults, 'value') };
                else return { data: searchResults };
            } else {
                const data = await getPromisedTrackSearch(
                    trimmedTerm,
                    labelIds,
                    100,
                    0
                );
                const searchResults = data?.map(
                    (row: GlobalSoundRecordingItem) => ({
                        label: `${row.item.name}-${row.item.isrc}`,
                        value: row.item.isrc,
                    })
                );
                return { data: searchResults };
            }
        }
        return {};
    };

    const onItemChange = (e: any) => {
        onChange({
            target: {
                name: selectName,
                value: e && e.map(({ value }: any) => value),
            },
        });
        setSelectedTracksWithinTerm(e);
        setAreTracksLoaded(true);
        if (!isEmpty(e)) {
            const tracksIsrc: any = [];
            e.forEach((track: any) => {
                tracksIsrc.push(track.value);
            });
        }
    };

    const onClose = () => {
        const newAttachmentsWithinContractAndTerm =
            getNewAttachmentsWithinContractAndTerm(
                isFeatureAbacusImproveTrackSearchEnabled
                    ? selectedTracksIsrcs
                    : selectedTracksIsrcsWithinTerm,
                originalAttachmentsWithinTerm,
                otherAttachmentsWithinContract
            );
        if (!isEmpty(newAttachmentsWithinContractAndTerm)) {
            getContractsTerms(
                accountId,
                newAttachmentsWithinContractAndTerm,
                CONTRACT_TERM_TYPES.TRACK.toLowerCase()
            );
        }
    };
    useEffect(() => {
        if (!isFeatureAbacusImproveTrackSearchEnabled) {
            if (isEmpty(tracksIsrcs)) {
                setSelectedTracksWithinTerm([]);
            } else setSelectedTracksIsrcsWithinTerm(tracksIsrcs);
        } else {
            if (!areTracksLoaded) setSelectedTracksIsrcsWithinTerm(tracksIsrcs);

            if (isEmpty(tracksIsrcs)) setSelectedTracksWithinTerm([]);
            else {
                const totalPages = Math.ceil(
                    tracksIsrcs.length / requestParams.limit
                );
                setRequestParams({ ...requestParams, totalPages });
                setSelectedTracksIsrcs(tracksIsrcs);
            }
        }
    }, [tracksIsrcs]);

    useEffect(() => {
        if (
            !isFeatureAbacusImproveTrackSearchEnabled &&
            !isEmpty(selectedTracksIsrcsWithinTerm)
        ) {
            const isrc = selectedTracksIsrcsWithinTerm[0];
            getTrackByIsrc(isrc);
        }
    }, [selectedTracksIsrcsWithinTerm]);

    useEffect(() => {
        if (
            isFeatureAbacusImproveTrackSearchEnabled &&
            !isEmpty(selectedTracksIsrcsWithinTerm) &&
            requestParams.currentPageCount <= requestParams.totalPages
        ) {
            const fetchTracks = async () => {
                const data = await getContractsTermsAttachments(
                    contractTermId,
                    requestParams.limit,
                    requestParams.offset
                );
                setTracksData(data);
            };
            fetchTracks();
        }
    }, [selectedTracksIsrcsWithinTerm, requestParams.currentPageCount]);

    useEffect(() => {
        if (
            trackData &&
            trackData?.globalSoundRecordingByIsrc &&
            !areTracksLoaded
        ) {
            const track = trackData?.globalSoundRecordingByIsrc;
            const item = {
                label: `${track.name}-${track.isrc}`,
                value: track.isrc,
            };
            setSelectedTracksWithinTerm([...selectedTracksWithinTerm, item]);
            if (selectedTracksIsrcsWithinTerm.length) {
                const newTracksIsrcs = selectedTracksIsrcsWithinTerm.slice(1);
                if (newTracksIsrcs.length)
                    setSelectedTracksIsrcsWithinTerm([
                        ...selectedTracksIsrcsWithinTerm.slice(1),
                    ]);
                else setAreTracksLoaded(true);
            }
        }
    }, [trackData]);

    useEffect(() => {
        if (
            !areTracksLoaded &&
            requestParams.currentPageCount <= requestParams.totalPages &&
            !isEmpty(tracksData)
        ) {
            let item: SelectedTrack;
            const tracks: SelectedTrack[] = [];
            tracksData &&
                tracksData?.forEach((track: any) => {
                    if (track.tracks.totalCount > 0) {
                        item = {
                            label: `${track.tracks.tracks[0]?.trackName}-${track.isrc}`,
                            value: track.isrc,
                        };
                    } else
                        item = {
                            label: `undefined-${track.isrc}`,
                            value: track.isrc,
                        };
                    tracks.push(item);
                });
            setSelectedTracksWithinTerm([
                ...selectedTracksWithinTerm,
                ...tracks,
            ]);
            setRequestParams({
                ...requestParams,
                currentPageCount: requestParams.currentPageCount + 1,
                offset: requestParams.offset + requestParams.limit,
            });
            if (requestParams.totalPages == requestParams.currentPageCount)
                setAreTracksLoaded(true);
        }
    }, [tracksData]);

    useEffect(() => {
        setErrors(validateAttachments());
    }, [
        accountContractTerms,
        selectedTracksIsrcsWithinTerm,
        selectedTracksIsrcs,
    ]);

    return (
        <MultiSelect
            className="track-select"
            controlId="trackSearch"
            id="trackSearch"
            onChange={onItemChange}
            onClose={onClose}
            onLoadOptions={performSearch}
            placeholder={SEARCH_PLACEHOLDER}
            selectedValue={selectedTracksWithinTerm}
            disabled={isEmpty(labelIds)}
            menuMinWidth="100%"
        />
    );
};

export default TrackMultiSelect;
