import { gql } from '@apollo/client';
import { Alert, Button, Form, useToast } from '@theorchard/suite-components';
import { formatMessage } from '@theorchard/suite-frontend';
import { pick } from 'lodash';
import React, { useEffect, useMemo, useState } from 'react';
import Modal from 'react-bootstrap/Modal';
import OAPanel from 'shared/components/oaPanel';
import EditTable from 'src/components/editTable/edit-table';
import InfoTable from 'src/components/infoTable/info-table';
import { STATEMENT_PERIODS_LIST_LIMIT } from 'src/constants';
import {
    useGetDictionariesQuery,
    useGetStatementPeriodsListQuery,
    useGetVendorByVendorIdAccountQuery,
    useUpdateVendorMutation,
} from 'src/data';
import { countryLabel, typeLabel } from 'src/data/dictionaries';
import { useUpdateVendorAccountNoteMutation } from 'src/data/mutations/__generated__/updateVendorAccountNote';
import { useUpdateVendorClosersMutation } from 'src/data/mutations/__generated__/updateVendorClosers';
import { useUpdateVendorFirstStatementPeriodMutation } from 'src/data/mutations/__generated__/updateVendorFirstStatementPeriod';
import { useUpdateVendorRelationshipNotesMutation } from 'src/data/mutations/__generated__/updateVendorRelationshipNotes';
import { GetVendorByVendorIdAccountDocument } from 'src/data/queries/__generated__/getVendorByIdAccount';
// import { isValidEmail } from 'src/utils/email-validation';
import { filterNullUndefined } from 'src/utils/filter-nulls-and-undefineds';
import { changeSpacesToUnderscoresAndUppercase } from 'src/utils/formatted-string-spaces-to-underscores';
import { removeUnderscoresAndCapitalize } from 'src/utils/remove-underscores-and-capitalize';
import { sanitizeRelationshipNotes } from 'src/utils/sanitize-relationship-notes';
import useLabelInfoConfig from '../config';
import type { VendorInput } from 'shared/data/schema';
import type { Dictionaries } from 'src/components/infoTable/types';
import type { GetDictionariesQuery } from 'src/data/queries/__generated__/getDictionaries';
import type { GetVendorByVendorIdAccountQuery } from 'src/data/queries/__generated__/getVendorByIdAccount';
import type { LabelInfoProps } from 'src/types';

const requiredFields = [
    'name',
    'parentCompanyUuid',
    'companyBrandId',
    'serviceTierUuid',
    'owner',
];

export const transformDictionaries = (
    dictionariesList: GetDictionariesQuery | undefined
): Dictionaries => ({
    genres:
        dictionariesList?.genres?.map(({ name, id }) => ({
            label: name,
            value: String(id),
        })) || [],
    serviceTypes:
        dictionariesList?.serviceTypes?.map(({ name, id }) => ({
            label: name,
            value: String(id),
        })) || [],
    countries:
        dictionariesList?.countries?.map(({ territoryName, orchId }) => ({
            label: territoryName,
            value: orchId,
        })) || [],
    regions:
        dictionariesList?.regions?.map(({ name, id }) => ({
            label: name,
            value: String(id),
        })) || [],
    labelIdentifiers:
        Object.keys(typeLabel)?.map(t => ({
            label: removeUnderscoresAndCapitalize(t),
            value: t,
        })) || [],
    defaultSalesSheets:
        Object.entries(countryLabel)?.map(([key, name]) => ({
            label: name,
            value: key,
        })) || [],
    assignedToList:
        dictionariesList?.assignedToList?.users?.map(
            ({ firstName, lastName, id }) => ({
                label: `${firstName} ${lastName}`,
                value: String(id),
            })
        ) || [],
    quarterbackLabelManagers:
        dictionariesList?.quarterbackLabelManagers?.users?.map(
            ({ firstName, lastName, id }) => ({
                label: `${firstName} ${lastName}`,
                value: String(id),
            })
        ) || [],

    assignedReviewerList:
        dictionariesList?.assignedReviewerList?.users?.map(
            ({ firstName, lastName, id }) => ({
                label: `${firstName} ${lastName}`,
                value: String(id),
            })
        ) || [],

    productManagers:
        dictionariesList?.productManagers?.users?.map(
            ({ firstName, lastName, id }) => ({
                label: `${firstName} ${lastName}`,
                value: String(id),
            })
        ) || [],
    owners:
        dictionariesList?.owners?.map(({ abbreviation }) => ({
            label: abbreviation,
            value: abbreviation,
        })) || [],
    companyBrands:
        dictionariesList?.companyBrands?.map(
            ({ uuid, displayName, parentCompany }) => ({
                label: displayName,
                value: uuid,
                parentCompany,
            })
        ) || [],
    parentCompanies:
        dictionariesList?.companyBrands?.map(({ parentCompany }) => ({
            label: parentCompany?.displayName || '',
            value: parentCompany?.uuid || '',
        })) || [],
    serviceTiers:
        dictionariesList?.serviceTiers?.map(({ uuid, displayName }) => ({
            label: displayName,
            value: uuid,
        })) || [],
    closers:
        dictionariesList?.closers?.users?.map(
            ({ firstName, lastName, id }) => ({
                label: `${firstName} ${lastName}`,
                value: String(id),
            })
        ) || [],
});

export const transformInitLabelDataForMutation = (
    vendor: LabelInfoProps,
    dictionaries: Dictionaries
) => ({
    contactEmail: vendor?.contactEmail || undefined,
    name: vendor?.name || undefined,
    contactName: vendor?.contactName || undefined,
    countryId: vendor?.countryId || undefined,
    regionId:
        dictionaries['regions']?.find(data => data?.label === vendor?.region)
            ?.value || undefined,
    estimatedTotalProducts: vendor?.estReleases || undefined,
    defaultSalesSheetId: vendor?.defaultSalesSheetTemplate?.id,
    soundScanCodeCA: vendor?.soundScanCodeCA,
    soundScanCodeUSA: vendor?.soundScanCodeUSA,
    estimatedTotalTracks: vendor?.estTracks || undefined,
    labelIdentifier:
        (vendor?.labelIdentifier &&
            changeSpacesToUnderscoresAndUppercase(vendor.labelIdentifier)) ||
        undefined,
    isOwned: vendor?.isOwned || undefined,
    owner: vendor?.owner || undefined,
    companyBrandId: vendor?.companyBrand?.uuid || undefined,
    parentCompanyUuid: vendor?.companyBrand?.parentCompany?.uuid || undefined,
    serviceTierUuid: vendor?.serviceTier?.uuid || undefined,
    primaryGenreId:
        dictionaries['genres'].find(genre => genre?.label === vendor?.genre)
            ?.value || undefined,
    priority: vendor?.priority || undefined,
    productManagerId: vendor?.productManager,
    quarterbackLabelManagerId: vendor?.quarterbackLabelManager || undefined,
    serviceTypeId: vendor?.activeVendorContract?.serviceType?.id || undefined,
    welcomeEmailSent: {
        date: vendor?.welcomeEmailDate,
        senderId: '',
    },
    assignedToId:
        dictionaries['assignedToList']?.find(
            data =>
                data?.label ===
                `${String(vendor?.assignedTo?.firstName)} ${String(
                    vendor?.assignedTo?.lastName
                )}`
        )?.value || undefined,
    assignedReviewerId:
        dictionaries['assignedReviewerList']?.find(
            data =>
                data?.label ===
                `${String(vendor?.assignedReviewer?.firstName)} ${String(
                    vendor?.assignedReviewer?.lastName
                )}`
        )?.value || undefined,
    supportContactEmail: vendor?.supportContactEmail || undefined,
    website: vendor?.website || undefined,
    labelSummary: vendor?.labelSummary || undefined,
});

export const HomePage = () => {
    const { vendorId } = useLabelInfoConfig();
    const { data: allDictionaries, loading: dictionariesLoading } =
        useGetDictionariesQuery();
    const {
        data: statementPeriodsData,
        fetchMore,
        loading: StatementPeriodLoading,
    } = useGetStatementPeriodsListQuery({
        variables: {
            limit: STATEMENT_PERIODS_LIST_LIMIT,
            offset: 0,
        },
    });

    const toast = useToast();
    const [updateVendorMutation, { error, loading }] =
        useUpdateVendorMutation();
    const [updateVendorNoteMutation] = useUpdateVendorAccountNoteMutation();
    const [updateVendorClosersMutation] = useUpdateVendorClosersMutation();
    const [updateVendorFirstStatementPeriodMutation] =
        useUpdateVendorFirstStatementPeriodMutation();
    const [updateVendorRelationshipNotesMutation] =
        useUpdateVendorRelationshipNotesMutation();
    const [alertError, setAlertError] = useState<string | undefined>(undefined);
    const [isEditDataLoaded, setEditDataLoaded] = useState(false);

    const { data, loading: queryLoading } = useGetVendorByVendorIdAccountQuery({
        variables: {
            vendorId: Number(vendorId),
        },
    });
    const shouldUpdateServiceType = data?.vendor?.activeVendorContract !== null;
    const [showModal, setShowModal] = useState(false);
    const [labelData, setLabelData] = useState({} as VendorInput);
    const [vendorNoteData, setVendorNoteData] = useState<{
        vendorId: string;
        note: string | null;
    }>({ vendorId: '', note: null });
    const [vendorClosers, setVendorClosers] = useState<{
        vendorId: string;
        closers: string[] | [];
    }>({ vendorId: '', closers: [] });
    const [firstStatementPeriodId, setFirstStatementPeriodId] = useState<
        string | undefined
    >(undefined);
    const [statementPeriods, setStatementPeriods] = useState<
        {
            statementPeriodId: string;
            statementPeriodName: string;
        }[]
    >([]);
    const [relationshipNotes, setRelationshipNotes] = useState<
        string | undefined
    >(undefined);

    const [hasErrors, setHasErrors] = useState<boolean>(false);

    const dictionaries: Dictionaries = useMemo(() => {
        const baseDict = transformDictionaries(allDictionaries);

        if (data?.vendor?.closerUsers) {
            const activeClosers = baseDict['closers'] ?? [];

            const vendorClosersWithInactive = data.vendor.closerUsers.map(
                user => ({
                    label: `${user.firstName ?? ''} ${user.lastName ?? ''}${
                        user.active === false ? ' (inactive)' : ''
                    }`.trim(),
                    value: String(user.id),
                })
            );

            baseDict['closers'] = [
                ...activeClosers,
                ...vendorClosersWithInactive.filter(
                    vendorCloser =>
                        !activeClosers.some(
                            active => active.value === vendorCloser.value
                        )
                ),
            ];
        }
        const productManagerUser = data?.vendor?.productManagerUser;
        if (productManagerUser?.active === false) {
            const activeProductManagers = baseDict['productManagers'] ?? [];
            if (
                !activeProductManagers.some(
                    pm => pm.value === String(productManagerUser.id)
                )
            ) {
                baseDict['productManagers'] = [
                    ...activeProductManagers,
                    {
                        label: `${productManagerUser.firstName ?? ''} ${productManagerUser.lastName ?? ''} (inactive)`.trim(),
                        value: String(productManagerUser.id),
                    },
                ];
            }
        }

        return baseDict;
    }, [
        allDictionaries,
        data?.vendor?.closerUsers,
        data?.vendor?.productManagerUser,
    ]);

    const labelDataForMutation = useMemo(
        () => transformInitLabelDataForMutation(data?.vendor, dictionaries),
        [data?.vendor, dictionaries]
    );

    useEffect(() => {
        if (error?.message) {
            toast(error?.message, { variant: 'danger' });
        }
    }, [error?.message]);

    useEffect(() => {
        if (!isEditDataLoaded && data?.vendor?.uuid && !dictionariesLoading) {
            setEditDataLoaded(true);
            setLabelData(labelDataForMutation as VendorInput);
            setVendorNoteData({
                vendorId,
                note: data?.vendor?.contentReviewNote || null,
            });
            setVendorClosers({
                vendorId,
                closers: (data?.vendor?.closerUsers ?? []).map(user =>
                    String(user.id)
                ),
            });
            setFirstStatementPeriodId(
                data?.vendor?.firstStatementPeriod?.statementPeriodId
            );
            setRelationshipNotes(data?.vendor?.relationshipNotes || undefined);
        }
    }, [
        data?.vendor,
        dictionaries,
        dictionariesLoading,
        isEditDataLoaded,
        labelDataForMutation,
        vendorId,
    ]);

    useEffect(() => {
        if (
            !StatementPeriodLoading &&
            statementPeriodsData?.abacusStatementPeriodsList?.items
        ) {
            const total =
                statementPeriodsData.abacusStatementPeriodsList.totalCount;
            const initialItems =
                statementPeriodsData.abacusStatementPeriodsList.items;

            setStatementPeriods(initialItems);

            (async () => {
                const limit = STATEMENT_PERIODS_LIST_LIMIT;
                let offset = limit;

                while (offset < total) {
                    const { data: moreData } = await fetchMore({
                        variables: { limit, offset },
                    });

                    const nextItems =
                        moreData?.abacusStatementPeriodsList?.items ?? [];

                    if (nextItems.length === 0) break;
                    setStatementPeriods(prev => [...prev, ...nextItems]);
                    offset += limit;
                }
            })();
        }
    }, [StatementPeriodLoading, statementPeriodsData, fetchMore]);

    const onFormSave = async () => {
        const required = requiredFields.find(key => {
            if (
                !labelData?.[key as keyof VendorInput] &&
                key === 'serviceTierUuid'
            ) {
                setAlertError(
                    'Account Service Tier is required. Please contact the Contract Admin team.'
                );
            }

            return !labelData?.[key as keyof VendorInput];
        });
        const isClosersValid =
            Array.isArray(vendorClosers.closers) &&
            vendorClosers.closers.length > 0;
        if (!isClosersValid) {
            setAlertError('At least one closer must be assigned to the label.');
        }
        const isFirstStatementPeriodValid =
            firstStatementPeriodId &&
            statementPeriods.some(
                sp => sp.statementPeriodId === firstStatementPeriodId
            );
        if (!isFirstStatementPeriodValid) {
            setAlertError(
                'You must select a Suppress Workstation Accounting Before period.'
            );
        }
        if (
            required ||
            !isClosersValid ||
            !isFirstStatementPeriodValid
            // || (labelData.contactEmail && !isValidEmail(labelData.contactEmail))
            // TODO return when Mike Lorenz will investigate whats going on with contact email
            // for AWAL labels which is used as a foreign key to an external database and doesn't pass validation
        ) {
            setHasErrors(true);
        } else {
            setHasErrors(false);
            setAlertError(undefined);

            const defaultSalesSheetId =
                // @ts-expect-error
                labelData.defaultSalesSheetId;
            // @ts-expect-error
            const serviceTypeId = labelData?.serviceTypeId ?? null;
            const clone = {
                ...filterNullUndefined(labelData),
                isOwned:
                    String(labelData?.isOwned) === 'Yes' ||
                    labelData?.isOwned === true,
            } as VendorInput;
            // @ts-expect-error
            delete clone.defaultSalesSheetId;
            delete clone.parentCompanyUuid;
            // @ts-expect-error
            delete clone.serviceTypeId;
            if (clone.assignedToId === null) {
                delete clone.assignedToId;
            }
            !clone?.welcomeEmailSent?.senderId && delete clone.welcomeEmailSent;
            await updateVendorNoteMutation({
                variables: {
                    vendorId,
                    note: vendorNoteData.note || '',
                },
                update: (cache, { data }) => {
                    const dataOld: GetVendorByVendorIdAccountQuery | null =
                        cache.readQuery({
                            query: GetVendorByVendorIdAccountDocument,
                            variables: {
                                vendorId: Number(vendorId),
                            },
                        });
                    const updatedVendor = {
                        ...dataOld?.vendor,
                        ...data?.updateVendorAccountNote,
                    };
                    cache.writeQuery({
                        query: GetVendorByVendorIdAccountDocument,
                        data: { vendor: updatedVendor },
                        variables: {
                            vendorId: Number(vendorId),
                        },
                    });
                },
            });
            await updateVendorClosersMutation({
                variables: {
                    uuid: data?.vendor?.uuid || '',
                    closers: vendorClosers.closers,
                },
                update: (cache, { data }) => {
                    const dataOld: GetVendorByVendorIdAccountQuery | null =
                        cache.readQuery({
                            query: GetVendorByVendorIdAccountDocument,
                            variables: {
                                vendorId: Number(vendorId),
                            },
                        });

                    const updatedVendor = {
                        ...dataOld?.vendor,
                        ...data?.updateVendorClosers,
                    };

                    cache.writeQuery({
                        query: GetVendorByVendorIdAccountDocument,
                        data: { vendor: updatedVendor },
                        variables: {
                            vendorId: Number(vendorId),
                        },
                    });
                },
            });
            await updateVendorFirstStatementPeriodMutation({
                variables: {
                    uuid: data?.vendor?.uuid || '',
                    firstStatementPeriodId: firstStatementPeriodId ?? '',
                },
                update: (cache, { data }) => {
                    const dataOld: GetVendorByVendorIdAccountQuery | null =
                        cache.readQuery({
                            query: GetVendorByVendorIdAccountDocument,
                            variables: {
                                vendorId: Number(vendorId),
                            },
                        });
                    const updatedVendor = {
                        ...dataOld?.vendor,
                        ...data?.updateVendorFirstStatementPeriod,
                    };

                    cache.writeQuery({
                        query: GetVendorByVendorIdAccountDocument,
                        data: { vendor: updatedVendor },
                        variables: {
                            vendorId: Number(vendorId),
                        },
                    });
                },
            });
            const cleanRelationshipNotes =
                sanitizeRelationshipNotes(relationshipNotes);
            await updateVendorRelationshipNotesMutation({
                variables: {
                    uuid: data?.vendor?.uuid || '',
                    relationshipNotes: cleanRelationshipNotes,
                },
                update: (cache, { data }) => {
                    const dataOld: GetVendorByVendorIdAccountQuery | null =
                        cache.readQuery({
                            query: GetVendorByVendorIdAccountDocument,
                            variables: {
                                vendorId: Number(vendorId),
                            },
                        });
                    const updatedVendor = {
                        ...dataOld?.vendor,
                        ...data?.updateVendorRelationshipNotes,
                    };

                    cache.writeQuery({
                        query: GetVendorByVendorIdAccountDocument,
                        data: { vendor: updatedVendor },
                        variables: {
                            vendorId: Number(vendorId),
                        },
                    });
                },
            });
            await updateVendorMutation({
                variables: {
                    id: data?.vendor?.uuid || '',
                    input: JSON.parse(JSON.stringify(clone)),
                    shouldUpdateServiceType,
                    defaultSalesSheetId,
                    serviceTypeId,
                    vendorId,
                },
                update: (cache, { data }) => {
                    const dataOld: GetVendorByVendorIdAccountQuery | null =
                        cache.readQuery({
                            query: GetVendorByVendorIdAccountDocument,
                            variables: {
                                vendorId: Number(vendorId),
                            },
                        });
                    const updatedVendor = {
                        ...dataOld?.vendor,
                        ...data?.updateVendor,
                    };
                    cache.writeQuery({
                        query: GetVendorByVendorIdAccountDocument,
                        data: { vendor: updatedVendor },
                        variables: {
                            vendorId: Number(vendorId),
                        },
                    });
                    // attempt to refresh query from OA header
                    cache.writeQuery({
                        query: gql`
                            query VendorBrandByVendorIdQuery($vendorId: Int!) {
                                vendor(vendorId: $vendorId) {
                                    name
                                    companyBrand(showOnDemand: true) {
                                        displayName
                                        name
                                        uuid
                                    }
                                    serviceTier(showOnDemand: true) {
                                        displayName
                                    }
                                    dateCreated
                                    lastUpdate
                                    labelIdentifier
                                }
                            }
                        `,
                        data: {
                            vendor: pick(updatedVendor, [
                                'name',
                                'companyBrand',
                                'serviceTier',
                                'dateCreated',
                                'labelIdentifier',
                                'lastUpdate',
                            ]),
                        },
                        variables: { vendorId: Number(vendorId) },
                    });
                },
            });
            setShowModal(false);
            setRelationshipNotes(cleanRelationshipNotes);
        }
    };

    return (
        <OAPanel
            title="Label Info"
            editable={false}
            loading={loading || queryLoading}
            onEdit={() => setShowModal(true)}
        >
            {
                <InfoTable
                    data={data?.vendor as LabelInfoProps}
                    dictionaries={dictionaries}
                />
            }
            <div className={`modal ${showModal ? 'show' : 'hide'}`}>
                <Modal.Dialog>
                    <Modal.Header>
                        <Modal.Title>
                            {labelData?.name?.toUpperCase()}
                        </Modal.Title>
                        <span className="modal-header-buttons">
                            <Button
                                className="modal-button"
                                variant="secondary"
                                onClick={() => {
                                    setAlertError(undefined);
                                    setHasErrors(false);
                                    setLabelData(
                                        labelDataForMutation as VendorInput
                                    );
                                    setShowModal(false);
                                    setVendorClosers({
                                        vendorId: data?.vendor?.uuid || '',
                                        closers: (
                                            data?.vendor?.closerUsers ?? []
                                        ).map(user => String(user.id)),
                                    });
                                    setFirstStatementPeriodId(
                                        data?.vendor?.firstStatementPeriod
                                            ?.statementPeriodId
                                    );
                                    setRelationshipNotes(
                                        data?.vendor?.relationshipNotes ||
                                            undefined
                                    );
                                }}
                                size="lg"
                            >
                                {formatMessage(
                                    `labelInfo.common.cancel`
                                ).toUpperCase()}
                            </Button>
                            <Button
                                className="modal-button"
                                variant="primary"
                                onClick={onFormSave}
                                size="lg"
                            >
                                {formatMessage(
                                    `labelInfo.common.save`
                                ).toUpperCase()}
                            </Button>
                        </span>
                    </Modal.Header>
                    <Modal.Body>
                        <Form validated>
                            <EditTable
                                labelData={labelData}
                                setLabelData={setLabelData}
                                setVendorNoteData={setVendorNoteData}
                                setVendorClosers={setVendorClosers}
                                setRelationshipNotes={setRelationshipNotes}
                                hasErrors={hasErrors}
                                dictionaries={dictionaries}
                                welcomeEmailDate={
                                    data?.vendor?.welcomeEmailDate
                                }
                                reviewContextNote={vendorNoteData.note}
                                closers={vendorClosers.closers}
                                assignedTo={data?.vendor?.assignedTo}
                                roles={allDictionaries?.oaUserRoles}
                                companyBrand={data?.vendor?.companyBrand?.name}
                                firstStatementPeriodId={firstStatementPeriodId}
                                setFirstStatementPeriodId={
                                    setFirstStatementPeriodId
                                }
                                paymentInterval={
                                    data?.vendor?.activeVendorContract
                                        ?.paymentInterval
                                }
                                statementPeriods={statementPeriods}
                                relationshipNotes={relationshipNotes}
                            />
                        </Form>
                    </Modal.Body>
                </Modal.Dialog>
            </div>
            {alertError && (
                <Alert
                    testId="label-form-error"
                    className="label-form-error"
                    variant="error"
                    text={alertError}
                />
            )}
        </OAPanel>
    );
};
