import React, { useEffect, useState, useMemo } from 'react';
import type { FC } from 'react';
import {
    Alert,
    Sidecar,
    Form,
    MultiSelect,
} from '@theorchard/suite-components';
import { GlyphIcon } from '@theorchard/suite-icons';
import { omit, size } from 'lodash';
import {
    OTHER_CANNED_RESPONSE_ID,
    REJECTION,
    REJECTION_CLOSING_ID,
    REJECTION_OPENING_ID,
    REJECTION_OPENING_ID_V2,
    REJECTION_MIDDLE_ID,
    PERMA_REJECTION_ID,
    WILL_NOT_DELIVER_ID,
    SKIP_CLOSING_STATEMENT_REASON_IDS,
} from 'src/constants';
import { useRejectProductMutation } from 'src/data/mutations/reject';
import { useUpsertTracksAudioAttributesMutation } from 'src/data/mutations/upsertTracksAudioAttributes';
import { useUpsertTracksRightsAttributesMutation } from 'src/data/mutations/upsertTracksRightsAttributes';
import { useCannedResponsesQuery, useFetchCannedNote } from 'src/data/queries';
import {
    getAudioAttributesInput,
    getRightsAttributesInput,
} from 'src/utils/attributes';
import { usePermaRejectionFF } from 'src/utils/features';
import { getLanguageDisplayName } from 'src/utils/language';
import { trackContentReviewEventMerged } from 'src/utils/product';
import type { Product } from 'src/data/queries/product/types';
import type { MergedProduct } from 'src/product/product';

export const CLASS_NAME = 'ProductModalRejectionSidecar';
const INTL_PREFIX = 'contentReview.productModalRejectionSidecar';

const REJECTION_REASON_DEFINITIONS_LINK =
    'https://docs.google.com/spreadsheets/d/1z5g9lfU9Yl33mYvLPwOJhzfKPcONdKgM1u2-w32xpCY/edit?gid=1065749050#gid=1065749050';

interface RejectionReasonOption {
    id: string;
    label: string;
    value: string;
}

export interface RejectionReasonOptions {
    [key: string]: {
        label: string;
        options: RejectionReasonOption[];
    };
}

interface RejectionReason {
    id: string;
    keyword: string;
    note: string;
}

export interface Props {
    isOpen: boolean;
    product: Product;
    mergedProduct: MergedProduct;
    productReviewQueueId: number;
    submittedByUserLanguage: string | null;
    onReviewCompleted: (status: string, eventType?: string) => void;
    closeModal: () => void;
}

const ProductModalRejectionSidecar: FC<Props> = ({
    product,
    mergedProduct,
    isOpen,
    closeModal,
    onReviewCompleted,
    productReviewQueueId,
    submittedByUserLanguage,
}) => {
    const isPermaRejectionFlagEnabled = usePermaRejectionFF();

    const [openingStatement, setOpeningStatement] = useState<string>('');
    const [middleStatement, setMiddleStatement] = useState<string>('');
    const [closingStatement, setClosingStatement] = useState<string>('');
    const [isConfirmDisabled, setIsConfirmDisabled] = useState<boolean>(true);
    const [isPreviewOpen, setIsPreviewOpen] = useState<boolean>(false);
    const [willNotDeliver, setWillNotDeliver] = useState<boolean>(false);
    const [additionalNotes, setAdditionalNotes] = useState<string>('');
    const [rejectionNotes, setRejectionNotes] = useState<RejectionReason[]>([]);
    const [selectedReasons, setSelectedReasons] = useState<
        RejectionReasonOption[]
    >([]);

    const [updateTracksAudioAttributeCallback] =
        useUpsertTracksAudioAttributesMutation();
    const [updateTracksRightsAttributeCallback] =
        useUpsertTracksRightsAttributesMutation();

    const { data: reasons } = useCannedResponsesQuery({
        reviewContext: REJECTION,
    });

    const fetchRejectionNote = useFetchCannedNote(
        submittedByUserLanguage || 'en'
    );

    const shouldShowRejectionV2 = Boolean(additionalNotes.trim().length);

    const shouldShowClosingStatement = !selectedReasons.some(reason =>
        SKIP_CLOSING_STATEMENT_REASON_IDS.includes(reason.value)
    );

    // On mount, populate the opening and closing statements
    useEffect(() => {
        if (!isOpen) return;

        const rejectionOpeningId = shouldShowRejectionV2
            ? REJECTION_OPENING_ID_V2
            : REJECTION_OPENING_ID;

        void fetchRejectionNote(rejectionOpeningId).then(({ noteText }) =>
            setOpeningStatement(noteText)
        );

        if (!middleStatement)
            void fetchRejectionNote(REJECTION_MIDDLE_ID).then(({ noteText }) =>
                setMiddleStatement(noteText)
            );

        if (!closingStatement)
            void fetchRejectionNote(REJECTION_CLOSING_ID).then(({ noteText }) =>
                setClosingStatement(noteText)
            );
    }, [
        isOpen,
        fetchRejectionNote,
        openingStatement,
        middleStatement,
        closingStatement,
        shouldShowRejectionV2,
    ]);

    // Enable/disable a "REJECT" button based on selected reasons and additional notes
    useEffect(() => {
        const shouldConfirmBeDisabled = selectedReasons.some(
            selection => selection.value === OTHER_CANNED_RESPONSE_ID
        )
            ? additionalNotes.length === 0
            : size(rejectionNotes) === 0;

        setIsConfirmDisabled(shouldConfirmBeDisabled);
    }, [rejectionNotes, selectedReasons, additionalNotes]);

    // Formatted options for the MultiSelect
    const rejectionReasonsOptions = useMemo(() => {
        const reasonsByCategory =
            reasons?.reduce((options, reason) => {
                const {
                    cannedResponseCategory: category,
                    cannedResponseId,
                    keyword,
                } = reason;

                options[category.id] ??= { label: category.name, options: [] };
                options[category.id].options.push({
                    id: category.id,
                    label: keyword,
                    value: cannedResponseId,
                });

                return options;
            }, {} as RejectionReasonOptions) ?? {};

        return isPermaRejectionFlagEnabled
            ? reasonsByCategory
            : omit(reasonsByCategory, PERMA_REJECTION_ID);
    }, [reasons, isPermaRejectionFlagEnabled]);

    // Formatted rejection to be saved on backend
    const formattedRejection = useMemo(() => {
        const input = rejectionNotes.map(
            reason => '[' + reason.keyword + '] ' + reason.note
        );

        if (additionalNotes.trim())
            input.push('[Additional Notes] ' + additionalNotes.trim());

        const conditionalClosingStatement = shouldShowClosingStatement
            ? closingStatement
            : '';

        return [
            openingStatement,
            input.join('\n'),
            conditionalClosingStatement,
        ].join('\n\n');
    }, [
        rejectionNotes,
        additionalNotes,
        openingStatement,
        closingStatement,
        shouldShowClosingStatement,
    ]);

    const formattedRejectionV2 = useMemo(() => {
        const formattedRejectionNotes = rejectionNotes.map(
            reason => '[' + reason.keyword + '] ' + reason.note
        );
        const input = [];
        if (additionalNotes.trim()) input.push(additionalNotes.trim());

        const conditionalClosingStatement = shouldShowClosingStatement
            ? closingStatement
            : '';

        return [
            openingStatement,
            input,
            middleStatement,
            formattedRejectionNotes.join('\n'),
            conditionalClosingStatement,
        ].join('\n\n');
    }, [
        rejectionNotes,
        additionalNotes,
        openingStatement,
        middleStatement,
        closingStatement,
        shouldShowClosingStatement,
    ]);

    const formattedRejectionVersion = shouldShowRejectionV2
        ? formattedRejectionV2
        : formattedRejection;

    const triggerRejectProductMutation = useRejectProductMutation(
        productReviewQueueId,
        formattedRejectionVersion,
        rejectionNotes.map(note => note.id),
        onReviewCompleted,
        willNotDeliver
    );

    if (!isOpen) return null;

    const handleRejectionReasonChange = async (
        selectedReasons: RejectionReasonOption[]
    ) => {
        setIsPreviewOpen(false);
        setSelectedReasons(selectedReasons);

        const willNotDeliver = selectedReasons.some(
            reason => reason.value === WILL_NOT_DELIVER_ID
        );
        setWillNotDeliver(willNotDeliver);

        const ids = rejectionNotes.map(note => note.id);
        const selectedIds = selectedReasons.map(reason => reason.value);

        const idToRemove = ids.find(id => !selectedIds.includes(id));
        const idToAdd = selectedIds.find(
            id => !ids.includes(id) && id !== OTHER_CANNED_RESPONSE_ID
        );

        if (idToRemove)
            setRejectionNotes(
                rejectionNotes.filter(reason => reason.id !== idToRemove)
            );

        if (idToAdd) {
            const fetchedNote = await fetchRejectionNote(idToAdd);

            setRejectionNotes(prevNotes => [
                ...prevNotes,
                {
                    id: fetchedNote.cannedResponseId,
                    keyword: fetchedNote.noteKeyword,
                    note: fetchedNote.noteText.replaceAll(
                        '{{productTitle}}',
                        mergedProduct.basics.productName.currentValue ||
                            product.productName
                    ),
                },
            ]);
        }
    };

    const handleFormSubmission = async () => {
        setIsConfirmDisabled(true);

        await triggerRejectProductMutation();
        await updateTracksAudioAttributeCallback({
            variables: {
                input: getAudioAttributesInput(
                    mergedProduct?.getTrackList(),
                    product.tracks
                ),
            },
        });
        await updateTracksRightsAttributeCallback({
            variables: {
                input: getRightsAttributesInput(
                    mergedProduct?.getTrackList(),
                    product.tracks
                ),
            },
        });

        closeModal();
    };

    const formatPreviewText = () => {
        return (
            <div className="preview-text">
                <span className="title">{openingStatement}</span>
                {Object.entries(rejectionNotes).map(([key, item]) => (
                    <span key={key}>
                        <p className="title">{item.keyword}</p>
                        <p className="body">{item.note.trim()}</p>
                    </span>
                ))}
                {!willNotDeliver && shouldShowClosingStatement && (
                    <span className="title">{closingStatement}</span>
                )}
            </div>
        );
    };

    const formatPreviewTextV2 = () => {
        return (
            <div className="preview-text">
                <span className="spanV2">
                    <p className="title">{openingStatement}</p>
                    <p className="body">{additionalNotes.trim()}</p>
                </span>

                <span className="spanV2">
                    <p className="body">{middleStatement}</p>
                </span>
                {Object.entries(rejectionNotes).map(([key, item]) => (
                    <span key={key} className="spanV2">
                        <p className="title">{item.keyword}</p>
                        <p className="body">{item.note.trim()}</p>
                    </span>
                ))}
                {!willNotDeliver && shouldShowClosingStatement && (
                    <span className="title">{closingStatement}</span>
                )}
            </div>
        );
    };

    const formatLanguageAlert = (language: string | null) => {
        if (!language || language === 'en') return;

        const languageName = getLanguageDisplayName(language);

        return languageName ? (
            <Alert
                variant="information"
                className="UserLanguageAlert"
                testId="UserLanguageAlert"
                text={
                    <span>
                        {$tx(`${INTL_PREFIX}.userLanguageAlert`, {
                            bold: ({ children }) => <b>{children}</b>,
                            language: languageName,
                        })}
                    </span>
                }
            />
        ) : null;
    };

    return (
        <Sidecar
            className={CLASS_NAME}
            isOpen={isOpen}
            onRequestClose={closeModal}
            title={$t(`${INTL_PREFIX}.title`)}
            testId={CLASS_NAME}
            onConfirm={handleFormSubmission}
            confirmDisabled={isConfirmDisabled}
            confirmTitle={$t(`${INTL_PREFIX}.confirm`)}
            cancelTitle={$t(`${INTL_PREFIX}.cancel`)}
        >
            <Form
                className={`${CLASS_NAME}-Form`}
                data-testid={`${CLASS_NAME}-Form`}
                style={{ width: '100%' }}
                onSubmit={e => {
                    e.preventDefault();
                }}
            >
                {formatLanguageAlert(submittedByUserLanguage)}
                <Form.Group data-testid="form-group-rejection-reason">
                    <Form.Label>
                        {$t(`${INTL_PREFIX}.rejectionReason`)}
                    </Form.Label>
                    <MultiSelect
                        testId="form-select-rejection-reason"
                        options={Object.values(rejectionReasonsOptions)}
                        onChange={reason =>
                            void handleRejectionReasonChange(reason)
                        }
                        onSelect={() =>
                            trackContentReviewEventMerged(
                                'Click',
                                'Rejection Reasons',
                                null,
                                mergedProduct,
                                null,
                                mergedProduct.isRevision
                            )
                        }
                        selectedValue={selectedReasons}
                        placeholder={$t(`${INTL_PREFIX}.rejectionReasonSelect`)}
                        menuMaxWidth={'98%'}
                    />
                    <p className={`${CLASS_NAME}-reason-definitions`}>
                        {$tx(`${INTL_PREFIX}.reasonDefinitions`, {
                            link: ({ children }) => (
                                <a
                                    className={`${CLASS_NAME}-reason-definitions-link`}
                                    href={REJECTION_REASON_DEFINITIONS_LINK}
                                    target="_blank"
                                    rel="noreferrer"
                                >
                                    {children}
                                </a>
                            ),
                        })}
                    </p>
                </Form.Group>
                {willNotDeliver && (
                    <Alert
                        testId={'PermaRejectAlert'}
                        className={`${CLASS_NAME}-perma-reject-warning`}
                        variant={'warn'}
                        text={
                            <>
                                <div
                                    className={`${CLASS_NAME}-perma-reject-warning-title`}
                                >
                                    {$tx(
                                        `${INTL_PREFIX}.permaRejectionWarning`
                                    )}
                                </div>
                                <div
                                    className={`${CLASS_NAME}-perma-reject-warning-text`}
                                >
                                    {$tx(`${INTL_PREFIX}.clickPermaReject`)}
                                </div>
                            </>
                        }
                    />
                )}
                <Form.Group>
                    <Form.Label>{$t(`${INTL_PREFIX}.instructions`)}</Form.Label>
                    <Form.Control
                        data-testid="form-control-additional-rejection-notes"
                        as="textarea"
                        rows={10}
                        value={additionalNotes}
                        placeholder={$t(
                            `${INTL_PREFIX}.additionalNotesPlaceholder`
                        )}
                        onChange={e => {
                            if (isPreviewOpen) setIsPreviewOpen(false);
                            setAdditionalNotes(e.currentTarget.value);
                        }}
                        required={selectedReasons.some(
                            selection =>
                                selection.value === OTHER_CANNED_RESPONSE_ID
                        )}
                    />
                </Form.Group>
                <Form.Group>
                    <Form.Label>
                        {!isConfirmDisabled && (
                            <div
                                className="preview-notes-label"
                                data-testid="preview-notes-label"
                                onClick={() => {
                                    if (!isPreviewOpen) {
                                        trackContentReviewEventMerged(
                                            'Click',
                                            'Preview Copy - Rejection Note',
                                            null,
                                            mergedProduct,
                                            null,
                                            mergedProduct.isRevision
                                        );
                                    }
                                    setIsPreviewOpen(currValue => !currValue);
                                }}
                            >
                                {$t(`${INTL_PREFIX}.previewCopy`)}
                                <GlyphIcon
                                    className="accordion-chevron"
                                    data-testid="accordion-chevron"
                                    name={
                                        isPreviewOpen
                                            ? 'doubleChevronUp'
                                            : 'doubleChevronDown'
                                    }
                                    size={12}
                                />
                            </div>
                        )}
                    </Form.Label>
                    {isPreviewOpen && (
                        <div
                            className="preview-notes-body"
                            data-testid="preview-notes-body"
                        >
                            {shouldShowRejectionV2
                                ? formatPreviewTextV2()
                                : formatPreviewText()}
                        </div>
                    )}
                </Form.Group>
            </Form>
        </Sidecar>
    );
};

export default ProductModalRejectionSidecar;
