import type { FC } from 'react';
import React from 'react';
import { Button as SuiteButton, Alert } from '@theorchard/suite-components';
import {
    formatMessage,
    Segment,
    useIdentity,
} from '@theorchard/suite-frontend';
import { GlyphIcon } from '@theorchard/suite-icons';
import { Decimal } from 'decimal.js';
import { some } from 'lodash';
import { Link, useHistory } from 'react-router-dom';
import CompositionAgreementsList from 'src/components/compositionAgreementsList';
import HeaderLabelPicker from 'src/components/headerLabelPicker';
import { SHOW_ALL_LABEL } from 'src/constants';
import { useCreateComposition, useUpdateComposition } from 'src/data/mutations';
import { useApplicationContext } from 'src/utils/applicationContext';
import {
    useLimitModifyAccessFF,
    useUncontrolledWritersFF,
} from 'src/utils/features';
import { emptySongWriter } from 'src/utils/helpers';
import { songsDraftsUrl, songsSubmittedUrl, songUrl } from 'src/utils/urls';
import AssociatedSoundRecordings from './associatedSoundRecordings';
import SongDetails from './details';
import { titleError, iswcError, alternateError } from './errors';
import {
    isCompletelyOwned,
    songPageHeader,
    gsrToAssociatedSoundRecording,
    pclsrToAssociatedSoundRecording,
    useGetClashedCompositions,
} from './helpers';
import type { LabelUuid } from 'src/data/schemaTypes';
import type {
    SongWriterState,
    AssociatedSoundRecording,
    Composition,
    AssociatedLabelSoundRecording,
} from 'src/types';

type CompositionWithLSR = Composition & {
    associatedLabelSoundRecordings: AssociatedLabelSoundRecording[];
};

const SEARCH_DEBOUNCE_INTERVAL = 500;

export interface Props {
    associatedSoundRecording: AssociatedSoundRecording | null;
    composition: CompositionWithLSR | null | undefined;
    debounceTime?: number;
}
// Workaround for React 18 + react-router-dom v5 type incompatibility
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const LinkAny = Link as any;

const NewSong: FC<Props> = ({
    associatedSoundRecording,
    composition,
    debounceTime = SEARCH_DEBOUNCE_INTERVAL,
}) => {
    const history = useHistory();
    const isNoControlledWritersAllowed = useUncontrolledWritersFF();
    const identity = useIdentity();
    const showPubSongId = !!identity.isEmployee;
    const isLimitedModifyAccess = useLimitModifyAccessFF();

    const createComposition = useCreateComposition();
    const updateComposition = useUpdateComposition(composition ?? null);
    const category = 'New Song';

    const [title, setTitle] = React.useState<string>(
        associatedSoundRecording?.name || composition?.title || ''
    );
    const [alternateTitles, setAlternateTitles] = React.useState<string[]>(
        composition?.alternateTitles.filter(a => a) || []
    );

    const defaultSongWriters = composition?.agreements.map(
        ({
            agreement: { id: agreementId, controlled, songWriter, publisher },
            split,
            hasMusicContribution,
            hasLyricsContribution,
            hasArrangerContribution,
            containsPublicDomain,
            containsSample,
        }) => ({
            id: songWriter.id,
            agreementId,
            controlled,
            split,
            legalName: songWriter?.legalName || '',
            hasMusicContribution,
            hasLyricsContribution,
            hasArrangerContribution,
            containsPublicDomain,
            containsSample,
            pro: songWriter.pro || '',
            ipi: songWriter.ipi ? songWriter.ipi.toString() : null,
            publisherName: publisher?.name ?? '',
            label: songWriter.vendor
                ? ({ uuid: songWriter.vendor.uuid } as LabelUuid)
                : undefined,
        })
    ) || [emptySongWriter];

    const [songWriters, setSongWriters] =
        React.useState<SongWriterState[]>(defaultSongWriters);
    const [iswc, setIswc] = React.useState(composition?.iswc || '');
    const [containsPublicDomain, setContainsPublicDomain] =
        React.useState<boolean>(composition?.containsPublicDomain || false);
    const [containsSample, setContainsSample] = React.useState<boolean>(
        composition?.containsSample || false
    );

    const labelOption = composition?.vendor
        ? {
              name: composition.vendor.name,
              uuid: composition.vendor.uuid,
              id: {
                  vendorId: composition.vendor.id.vendorId,
                  subaccountId: 0,
              },
          }
        : null;

    const {
        label,
        labelAlt: labelAltAppContext,
        setLabelAlt: setLabelStaticAppContext,
    } = useApplicationContext();
    const selectedLabel =
        !label || label?.name === SHOW_ALL_LABEL ? labelAltAppContext : label;
    if (labelOption && labelAltAppContext?.uuid !== labelOption.uuid)
        setLabelStaticAppContext(labelOption);

    let initialSounds: AssociatedSoundRecording[] = [];

    const labelSoundRecordings = composition?.associatedLabelSoundRecordings;
    const globalSoundRecordings = composition?.associatedGlobalSoundRecordings;

    if (associatedSoundRecording?.id)
        initialSounds = [associatedSoundRecording];
    else if (globalSoundRecordings)
        globalSoundRecordings.forEach(gsr => {
            initialSounds.push(gsrToAssociatedSoundRecording(gsr));
        });

    if (labelSoundRecordings)
        labelSoundRecordings.forEach(lsr => {
            initialSounds.push(pclsrToAssociatedSoundRecording(lsr));
        });

    const [associatedSounds, setAssociatedSounds] =
        React.useState<AssociatedSoundRecording[]>(initialSounds);
    const [hasSubmitted, setHasSubmitted] = React.useState<boolean>(false);

    const onSave = (draft: boolean) => {
        const agreements = songWriters
            .map(sw => ({
                agreementId: sw.agreementId,
                split: sw.split ?? '',
                hasMusicContribution: sw.hasMusicContribution ?? false,
                hasArrangerContribution: sw.hasArrangerContribution ?? false,
                hasLyricsContribution: sw.hasLyricsContribution ?? false,
                containsPublicDomain:
                    containsPublicDomain && sw.containsPublicDomain,
                containsSample: containsSample && sw.containsSample,
                label: sw.label,
            }))
            .filter(sw => sw.agreementId.length > 0);

        const compositionData = {
            title,
            draft,
            alternateTitles,
            iswc: iswc || null,
            label: { uuid: label?.uuid },
            associatedLabelSoundRecordings: associatedSounds
                .filter(s => s.isLsr)
                .map(s => s.id),
            associatedGlobalSoundRecordings: associatedSounds
                .filter(s => !s.isLsr)
                .map(s => s.id),
            agreements,
            containsPublicDomain,
            containsSample,
        };

        if (composition?.id) {
            void Segment.trackEvent('Click - Update Song', {
                category: 'Update Song',
                title,
                alternateTitles,
                iswc,
                draftSong: draft,
            });
            void updateComposition({
                variables: {
                    id: composition.id,
                    ...compositionData,
                },
            });
        } else {
            void Segment.trackEvent('Click - Create Song', {
                category,
                title,
                alternateTitles,
                iswc,
            });
            void createComposition({ variables: compositionData });
        }

        return true;
    };

    const isrcDuplicateCompositionId = useGetClashedCompositions(
        composition?.id ?? null,
        associatedSounds,
        selectedLabel?.id.vendorId ?? 0
    );
    const anyAlternateErrors = some(alternateTitles, alternateError);
    const hasDraftError =
        titleError(title) ||
        iswcError(iswc) ||
        anyAlternateErrors ||
        (label === null && labelAltAppContext === null) ||
        isrcDuplicateCompositionId;

    const onSaveDraft = () => {
        if (hasDraftError) return false;

        onSave(true);
        return true;
    };

    const notCompletelyOwned = !isCompletelyOwned(
        songWriters.map(sw => new Decimal(sw.split || 0))
    );
    const noMusicContribution = !songWriters.some(
        sw => sw.hasMusicContribution
    );
    const noControlledWriters =
        !songWriters.some(sw => sw.controlled) && !isNoControlledWritersAllowed;
    const notEveryWriterContributed = !songWriters.every(
        sw =>
            sw.hasMusicContribution ||
            sw.hasLyricsContribution ||
            sw.hasArrangerContribution
    );

    const notEveryWriterVendorAsCompositionVendor =
        songWriters.some(songWriter => songWriter.label?.uuid) &&
        !songWriters.every(
            sw => !sw.label || sw.label?.uuid === selectedLabel?.uuid
        );
    const notCompleteContainsPublicDomain =
        containsPublicDomain &&
        !songWriters.some(sw => sw.containsPublicDomain);
    const notCompleteContainsSample =
        containsSample && !songWriters.some(sw => sw.containsSample);
    const hasSubmitError =
        notCompletelyOwned ||
        noMusicContribution ||
        noControlledWriters ||
        notEveryWriterContributed ||
        notEveryWriterVendorAsCompositionVendor ||
        (label === null && labelAltAppContext === null) ||
        notCompleteContainsPublicDomain ||
        notCompleteContainsSample ||
        isrcDuplicateCompositionId;

    const onSubmit = () => {
        setHasSubmitted(true);

        if (hasDraftError || hasSubmitError) return false;

        onSave(false);
        return true;
    };

    const songHeader = songPageHeader(composition ?? null);
    return (
        <div className="NewSong">
            <div className="AppHeader">
                <HeaderLabelPicker />
                <div>
                    <SuiteButton
                        onClick={() =>
                            composition?.id && composition?.draft === false
                                ? history.push(songsSubmittedUrl(1))
                                : history.push(songsDraftsUrl(1))
                        }
                    >
                        {formatMessage('generic.cancel')}
                    </SuiteButton>

                    {composition?.draft !== false && (
                        <SuiteButton
                            onClick={onSaveDraft}
                            disabled={isLimitedModifyAccess}
                        >
                            {formatMessage('generic.saveDraft')}
                        </SuiteButton>
                    )}

                    <SuiteButton
                        variant="primary"
                        onClick={onSubmit}
                        disabled={isLimitedModifyAccess}
                        data-testid={
                            composition?.id && composition?.draft === false
                                ? 'update-song-button'
                                : 'create-song-button'
                        }
                    >
                        {composition?.id && composition?.draft === false
                            ? formatMessage('generic.update')
                            : formatMessage('generic.submit')}
                    </SuiteButton>
                </div>
            </div>

            <div className="px-3">
                <form>
                    {hasSubmitted && hasSubmitError && (
                        <Alert
                            title={formatMessage('generic.error')}
                            variant="error"
                            text={
                                <>
                                    {!title.trim() && (
                                        <div>
                                            {formatMessage(
                                                'errors.songTitleIsRequired'
                                            )}
                                        </div>
                                    )}
                                    {notCompletelyOwned && (
                                        <div>
                                            {formatMessage(
                                                'errors.ownershipNot100'
                                            )}
                                        </div>
                                    )}
                                    {noMusicContribution && (
                                        <div>
                                            {formatMessage(
                                                'errors.noMusicContribution'
                                            )}
                                        </div>
                                    )}
                                    {noControlledWriters && (
                                        <div>
                                            {formatMessage(
                                                'errors.noControlledSongWriters'
                                            )}
                                        </div>
                                    )}
                                    {notEveryWriterContributed && (
                                        <div>
                                            {formatMessage(
                                                'errors.writerHasNotContributed'
                                            )}
                                        </div>
                                    )}
                                    {notEveryWriterVendorAsCompositionVendor && (
                                        <div>
                                            {formatMessage(
                                                'errors.allWritersSameAccountAsComposition',
                                                {
                                                    accountName:
                                                        selectedLabel?.name ||
                                                        '',
                                                }
                                            )}
                                        </div>
                                    )}
                                    {label === null &&
                                        labelAltAppContext === null && (
                                            <div>
                                                {formatMessage(
                                                    'errors.hasNoLabelSet'
                                                )}
                                            </div>
                                        )}
                                    {notCompleteContainsPublicDomain && (
                                        <div>
                                            {formatMessage(
                                                'errors.notCompleteContainsPublicDomain'
                                            )}
                                        </div>
                                    )}
                                    {notCompleteContainsSample && (
                                        <div>
                                            {formatMessage(
                                                'errors.notCompleteContainsSample'
                                            )}
                                        </div>
                                    )}
                                    {isrcDuplicateCompositionId && (
                                        <div>
                                            <span>
                                                {formatMessage(
                                                    'newSong.sameIsrcSongExists'
                                                )}
                                            </span>
                                            <LinkAny
                                                to={songUrl(
                                                    isrcDuplicateCompositionId
                                                )}
                                            >
                                                <SuiteButton
                                                    variant="link"
                                                    size="sm"
                                                >
                                                    {formatMessage(
                                                        'newSong.goToSong'
                                                    )}
                                                    <GlyphIcon
                                                        name="arrowRight"
                                                        size={12}
                                                    />
                                                </SuiteButton>
                                            </LinkAny>
                                        </div>
                                    )}
                                </>
                            }
                        />
                    )}

                    <div className="NewSong-title">
                        {formatMessage(songHeader)}
                    </div>

                    <SongDetails
                        compositionId={composition?.id}
                        title={title}
                        setTitle={setTitle}
                        iswc={iswc}
                        setIswc={setIswc}
                        alternateTitles={alternateTitles}
                        setAlternateTitles={setAlternateTitles}
                        containsPublicDomain={containsPublicDomain}
                        setContainsPublicDomain={setContainsPublicDomain}
                        containsSample={containsSample}
                        setContainsSample={setContainsSample}
                        showPubSongId={showPubSongId}
                        pubSongId={composition?.pubSongId}
                    />

                    <CompositionAgreementsList
                        label={selectedLabel}
                        songWriters={songWriters}
                        onChange={setSongWriters}
                        containsPublicDomain={containsPublicDomain}
                        containsSample={containsSample}
                        isSubmitted={hasSubmitted}
                    />

                    <AssociatedSoundRecordings
                        associatedSounds={associatedSounds}
                        setAssociatedSounds={setAssociatedSounds}
                        debounceTime={debounceTime}
                        category={category}
                        isrcDuplicateId={isrcDuplicateCompositionId || ''}
                    />
                </form>
            </div>
        </div>
    );
};

export default NewSong;
