import React from 'react';
import { formatMessage } from '@theorchard/suite-frontend';
import { cloneDeep } from 'lodash';
import { type WriterAgreement } from 'src/types';
import type { Publisher } from 'src/data/queries';
import type {
    CompositionAgreementPublisherInput,
    UpdateSongWriterAgreementsInput,
} from 'src/data/schemaTypes';

export interface AssociatedComposition {
    id: string;
    title: string;
    publisherName: string | null;
    publisherId: string | null;
    agreementId: string;
    controlled: boolean | null;
}

export const getCompositionsFromSelectedRows = (
    selectedRows: string[],
    compositions: AssociatedComposition[]
): AssociatedComposition[] =>
    compositions.filter(composition =>
        selectedRows.find(
            selectedRow =>
                `${composition.id}/${composition.agreementId}` === selectedRow
        )
    );

export type CompositionAgreementPubisherInputWithPublisherName =
    CompositionAgreementPublisherInput & {
        publisherName?: string | null;
    };

export type UpdateSongWriterAgreementsInputWithPublisherName = Omit<
    UpdateSongWriterAgreementsInput,
    'agreements'
> & {
    agreements: CompositionAgreementPubisherInputWithPublisherName[];
};

export const updateCompositionAgreementsInputWithSelectedPublisher = (
    agreementsToUpdate: CompositionAgreementPubisherInputWithPublisherName[],
    agreements: CompositionAgreementPubisherInputWithPublisherName[],
    selectedPublisher?: Publisher
): CompositionAgreementPubisherInputWithPublisherName[] => {
    agreementsToUpdate.forEach(agreementToUpdate => {
        const index = agreements.findIndex(
            newAgreement =>
                newAgreement.agreementUuid ===
                    agreementToUpdate.agreementUuid &&
                newAgreement.compositionUuid ===
                    agreementToUpdate.compositionUuid
        );
        if (index !== -1) {
            agreements[index].publisherUuid = selectedPublisher?.id;
            agreements[index].publisherName = selectedPublisher?.name || null;
            agreements[index].controlled = agreementToUpdate.controlled;
        } else {
            agreements.push({
                agreementUuid: agreementToUpdate.agreementUuid,
                compositionUuid: agreementToUpdate.compositionUuid,
                publisherUuid: selectedPublisher?.id,
                publisherName: selectedPublisher?.name || null,
                controlled: agreementToUpdate.controlled,
            });
        }
    });

    return agreements;
};

export const getAssociatedCompositionsToUpdate = (
    editedAssociatedComposition: AssociatedComposition | undefined,
    selectedPublisher: Publisher | undefined,
    selectedCompositionIds: string[],
    associatedCompositions: AssociatedComposition[]
): CompositionAgreementPublisherInput[] => {
    return editedAssociatedComposition
        ? [
              {
                  agreementUuid: editedAssociatedComposition.agreementId,
                  compositionUuid: editedAssociatedComposition.id,
                  publisherUuid: selectedPublisher?.id,
                  controlled: editedAssociatedComposition.controlled || false,
              },
          ]
        : getCompositionsFromSelectedRows(
              selectedCompositionIds,
              associatedCompositions
          ).map(associatedComposition => ({
              agreementUuid: associatedComposition.agreementId,
              compositionUuid: associatedComposition.id,
              publisherUuid: selectedPublisher?.id,
              controlled: associatedComposition.controlled || false,
          }));
};

export const updatePublisherDataForAssociatedCompositions = (
    associatedCompositionsToUpdate: CompositionAgreementPublisherInput[],
    associatedCompositions: AssociatedComposition[],
    selectedPublisher?: Publisher
): AssociatedComposition[] => {
    associatedCompositionsToUpdate.forEach(associatedCompositionToUpdate => {
        const index = associatedCompositions.findIndex(
            associatedComposition => {
                return (
                    associatedComposition.agreementId ===
                        associatedCompositionToUpdate.agreementUuid &&
                    associatedComposition.id ===
                        associatedCompositionToUpdate.compositionUuid
                );
            }
        );
        if (index !== -1) {
            associatedCompositions[index].publisherName =
                selectedPublisher?.name || null;
            associatedCompositions[index].publisherId =
                selectedPublisher?.id || null;
        }
    });

    return associatedCompositions;
};

export const formatAssociatedComps = (
    agreements: WriterAgreement[]
): AssociatedComposition[] =>
    agreements
        .map(a => {
            if (!a.compositions.length) return null;
            return a.compositions.map(c => ({
                id: c.id,
                title: c.title,
                publisherName: a.publisher?.name ?? null,
                publisherId: a.publisher?.id ?? null,
                agreementId: a.id,
                controlled: a.controlled,
            }));
        })
        .filter(c => c !== null)
        .flatMap(c => c)
        .sort((a, b) =>
            a.title.toLowerCase().localeCompare(b.title.toLowerCase())
        );

export const moveAssociatedComposition = (
    composition: AssociatedComposition,
    willBeControlled: boolean,
    setCompsFrom: React.Dispatch<React.SetStateAction<AssociatedComposition[]>>,
    setCompsTo: React.Dispatch<React.SetStateAction<AssociatedComposition[]>>,
    setUpdateSongWriterAgreementsInput: React.Dispatch<
        React.SetStateAction<UpdateSongWriterAgreementsInput | undefined>
    >,
    showAlert: boolean = true,
    showAlertFn: (message: string) => void,
    labelUuid: string,
    songWriterUuid: string
) => {
    const copyOfComposition = cloneDeep(composition);
    setCompsFrom(prevComps => {
        const copyOfPrevComps = cloneDeep(prevComps);
        return copyOfPrevComps.filter(
            prevComp =>
                prevComp.id !== copyOfComposition.id ||
                prevComp.agreementId !== copyOfComposition.agreementId
        );
    });

    setCompsTo(prevComps => {
        const copyOfPrevComps = cloneDeep(prevComps);
        const copyOfComposition = cloneDeep(composition);
        copyOfPrevComps.push({
            ...copyOfComposition,
            controlled: willBeControlled,
        });
        return copyOfPrevComps;
    });

    setUpdateSongWriterAgreementsInput(prev => {
        const agreements = cloneDeep(prev?.agreements) || [];

        return {
            label: { uuid: labelUuid },
            agreements: updateCompositionAgreementsInputWithSelectedPublisher(
                [
                    {
                        agreementUuid: copyOfComposition.agreementId,
                        compositionUuid: copyOfComposition.id,
                        controlled: willBeControlled,
                    },
                ],
                agreements,
                copyOfComposition.publisherId
                    ? {
                          id: copyOfComposition.publisherId,
                          name: copyOfComposition.publisherName,
                      }
                    : undefined
            ),
            songWriterUuid,
        };
    });

    if (showAlert) {
        showAlertFn(
            formatMessage('songWriterManager.compsMoved', {
                number: 1,
                controlledStatus: willBeControlled
                    ? 'Controlled'
                    : 'Non-Controlled',
            })
        );
    }
};

export const moveAssociatedCompositions = (
    setCompsFrom: React.Dispatch<React.SetStateAction<AssociatedComposition[]>>,
    setCompsTo: React.Dispatch<React.SetStateAction<AssociatedComposition[]>>,
    setUpdateSongWriterAgreementsInput: React.Dispatch<
        React.SetStateAction<UpdateSongWriterAgreementsInput | undefined>
    >,
    showAlertFn: (message: string) => void,
    setSelectedRows: React.Dispatch<React.SetStateAction<string[]>>,
    selectedRows: string[],
    associatedCompositions: AssociatedComposition[],
    labelUuid: string,
    songWriterUuid: string,
    newControlledStatus: 'Controlled' | 'Non-Controlled'
) => {
    const willBeControlled = newControlledStatus === 'Controlled';
    const showAlert = false;
    for (const associatedComposition of associatedCompositions) {
        if (
            !selectedRows.includes(
                associatedComposition.id +
                    '/' +
                    associatedComposition.agreementId
            )
        ) {
            continue;
        }
        moveAssociatedComposition(
            associatedComposition,
            willBeControlled,
            setCompsFrom,
            setCompsTo,
            setUpdateSongWriterAgreementsInput,
            showAlert,
            showAlertFn,
            labelUuid,
            songWriterUuid
        );
    }

    setSelectedRows([]);

    showAlertFn(
        formatMessage('songWriterManager.compsMoved', {
            number: selectedRows.length,
            controlledStatus: newControlledStatus,
        })
    );
};

export const mapAssociatedCompositions = (
    associatedCompositions: AssociatedComposition[],
    type: 'controlled' | 'nonControlled',
    updateSongWriterAgreementsInput?: UpdateSongWriterAgreementsInputWithPublisherName,
    selectedPublisher?: Publisher
) => {
    return associatedCompositions
        .filter(associatedComposition => {
            const updatedAssociatedComposition =
                updateSongWriterAgreementsInput?.agreements.find(
                    agreement =>
                        agreement.agreementUuid ===
                            associatedComposition.agreementId &&
                        agreement.compositionUuid === associatedComposition.id
                );

            const isControlled = updatedAssociatedComposition
                ? updatedAssociatedComposition.controlled
                : associatedComposition.controlled;
            const publisherId = updatedAssociatedComposition
                ? updatedAssociatedComposition.publisherUuid
                : associatedComposition.publisherId;

            if (!selectedPublisher) {
                return type === 'controlled' ? isControlled : !isControlled;
            }

            if (selectedPublisher.id === '0') {
                return (
                    (type === 'controlled' ? isControlled : !isControlled) &&
                    !publisherId
                );
            }

            return (
                (type === 'controlled' ? isControlled : !isControlled) &&
                publisherId === selectedPublisher.id
            );
        })
        .map(associatedComposition => {
            const updatedAssociatedComposition =
                updateSongWriterAgreementsInput?.agreements.find(
                    agreement =>
                        agreement.agreementUuid ===
                            associatedComposition.agreementId &&
                        agreement.compositionUuid === associatedComposition.id
                );

            return {
                ...associatedComposition,
                publisherId: updatedAssociatedComposition
                    ? updatedAssociatedComposition?.publisherUuid || null
                    : associatedComposition.publisherId,
                publisherName: updatedAssociatedComposition
                    ? updatedAssociatedComposition.publisherName || null
                    : associatedComposition.publisherName,
            };
        });
};

export const removePublisherNameFromAgreementsInput = (
    agreements: CompositionAgreementPubisherInputWithPublisherName[]
) => {
    return agreements.map(agreement => {
        const { publisherName, ...rest } = agreement;
        return rest;
    });
};

export const generateEditPublishingCompanyDescription = (
    isAllCompsWithPublisher: boolean,
    isAllCompsWithoutPublisher: boolean,
    selectedCompositions: AssociatedComposition[],
    editedAssociatedComposition?: AssociatedComposition
) => {
    if (
        isAllCompsWithPublisher ||
        (!isAllCompsWithPublisher && !isAllCompsWithoutPublisher)
    ) {
        const publishers = (
            <b>
                {editedAssociatedComposition
                    ? editedAssociatedComposition.publisherName
                    : [
                          ...new Set(
                              selectedCompositions
                                  .filter(c => c.publisherName)
                                  .map(composition => composition.publisherName)
                          ),
                      ].join(', ')}
            </b>
        );

        if (isAllCompsWithPublisher)
            return (
                <>
                    {formatMessage(
                        'songWriterManager.currentPublishingCompaniesInSelection'
                    )}
                    {publishers}
                </>
            );
        else
            return (
                <>
                    {formatMessage(
                        'songWriterManager.currentMixedPublishingCompanies1'
                    )}
                    {publishers}.<br />
                    {formatMessage(
                        'songWriterManager.currentMixedPublishingCompanies2'
                    )}
                </>
            );
    }

    return (
        <>{formatMessage('songWriterManager.currentNoPublishingCompanies')}</>
    );
};
