// 'use no memo' — see src/components/reference/reference.tsx for rationale.
// Page.Nav + Page.Body tab pattern depends on parent re-render on URL change.
'use no memo';

import React, { useState, useEffect, useRef, useCallback } from 'react';
import {
    Alert,
    Button,
    Form,
    Modal,
    Section,
    useToast,
} from '@theorchard/suite-components';
import { PageHeader } from '@theorchard/suite-components';
import { Page } from '@theorchard/suite-frontend';
import { capitalize } from 'lodash-es';
import { useHistory, useParams } from 'react-router-dom';
import { useSoftDeleteContractTerm } from 'src/apollo/mutations/contract-term';
import { useDeleteContractTermConditions } from 'src/apollo/mutations/contract-term-condition';
import { useContractTerm } from 'src/apollo/queries/contract-term';
import { ContractTermsDistroAttachmentsSelect } from 'src/components/contract-terms-refactored/contract-terms-distro-attachments-select';
import { ContractTermsDistroConditionsList } from 'src/components/contract-terms-refactored/contract-terms-distro-conditions-list';
import {
    CONTRACT_TERM_RATE,
    CONTRACT_TERMS_DISTRO_TERM_NAME_ERROR_MSG,
} from 'src/constants';
import { useContractBreadcrumbs } from 'src/hooks/breadcrumbs/contract';
import { useSaveConditionsHandler } from 'src/hooks/contract-terms/contract-condition';
import { useSaveHandler } from 'src/hooks/contract-terms/contract-term';
import { getContractDetail } from 'src/urls/frontend-royalties';
import { ContractTermName } from './contract-terms-distro-term-name';
import { VendorSearch } from './contract-terms-distro-vendor-search';
import type { AbacusContractTermConditionInput } from 'src/apollo/definitions/globalTypes';
import type { AttachmentDetail } from 'src/components/contract-detail/contract-terms-detail-presentational';
import type { AbacusDistroContractTermCondition } from 'src/types/abacus-contract-term';
import type { ContractTermFormItem } from 'src/types/abacus-contract-term-form-item';
import type { AbacusContractTermsInputErrors } from 'src/types/abacus-contract-terms-distro-input-errors';
import type { VendorSearchItem } from 'src/types/abacus-vendor';

export interface ContractTermsDistroFormFieldsPropTypes {
    contractTermFormItem?: ContractTermFormItem;
}

interface ErrorMessagesProps {
    serverError: string | null;
    errors: Record<string, string>;
}

const ErrorMessages: React.FC<ErrorMessagesProps> = ({
    serverError,
    errors,
}) => {
    const hasSpecificErrors = Object.keys(errors).length > 0;

    if (!serverError && !hasSpecificErrors) {
        return null;
    }

    return (
        <>
            {serverError && (
                <>
                    {serverError}
                    <br />
                </>
            )}
            {hasSpecificErrors && (
                <>
                    {Object.entries(errors).map(([key, errorMsg], index) => (
                        <span key={key || index}>
                            {typeof errorMsg === 'string'
                                ? errorMsg
                                : `An unknown error occurred for ${key}.`}
                            <br />
                        </span>
                    ))}
                </>
            )}
            {!serverError && !hasSpecificErrors && (
                <>
                    An error occurred while fetching the contract term.
                    <br />
                </>
            )}
            If the error persists, please contact the Abacus team.
        </>
    );
};

export const ContractTermsDistroFormFields: React.FC<
    ContractTermsDistroFormFieldsPropTypes
> = ({ contractTermFormItem: initialFormItem }) => {
    const { contractId, termType, contractTermId } = useParams<{
        contractId: string;
        termType: string;
        contractTermId?: string;
    }>();
    const history = useHistory();
    const toast = useToast();

    const [contractTermFormItem, setContractTermFormItem] =
        useState<ContractTermFormItem>(
            initialFormItem ?? {
                contractId,
                contractTermId: undefined,
                vendor: undefined,
                attachments: [],
                contractTermName: null,
                termType: termType || undefined,
                attachmentsRelations: null,
                conditions: [],
                newConditions: [],
            }
        );

    const [editableConditions, setEditableConditions] = useState<
        AbacusDistroContractTermCondition[]
    >(() => {
        if (initialFormItem && initialFormItem.conditions) {
            return initialFormItem.conditions.map(cond => ({
                contractTermConditionId: cond.contractTermConditionId,
                contractTermConditionName: cond.contractTermConditionName,
                contractTermId: cond.contractTermId,
                priority: cond.priority,
                termRate: String(cond.termRate),
                conditions: cond.conditions,
            })) as AbacusDistroContractTermCondition[];
        }
        return [];
    });

    const originalConditionIdsRef = useRef<string[]>(
        initialFormItem && initialFormItem.conditions
            ? (initialFormItem.conditions
                  .map(c => c.contractTermConditionId)
                  .filter((id): id is string => Boolean(id)) as string[])
            : []
    );

    const [errors, setErrors] = useState<AbacusContractTermsInputErrors>({});
    const [serverError, setServerError] = useState<string | null>(null);
    const [createdContractTermId, setCreatedContractTermId] = useState<
        string | null
    >(null);
    const [isSaveModalOpen, setIsSaveModalOpen] = useState<boolean>(false);
    const [isDeleteModalOpen, setIsDeleteModalOpen] = useState<boolean>(false);
    const [isSaveDisabled, setIsSaveDisabled] = useState<boolean>(true);
    const { data: fetchedTermData, loading: fetchLoading } = useContractTerm(
        contractTermId || ''
    );
    const saveHandler = useSaveHandler(contractId);
    const saveConditions = useSaveConditionsHandler(
        contractId,
        createdContractTermId || contractTermFormItem.contractTermId || ''
    );
    const deleteContractTermConditions = useDeleteContractTermConditions(
        contractId,
        contractTermFormItem.contractTermId || ''
    );

    const isEditForm = Boolean(contractTermId);
    const pageTitle = `${isEditForm ? 'Edit' : 'Add'} ${capitalize(termType)} Term`;
    const rawBreadcrumbs = useContractBreadcrumbs(pageTitle);
    const breadcrumbs = rawBreadcrumbs.map(bc => ({
        ...bc,
        label: bc.label ?? '',
    }));
    const deleteContractTerm = useSoftDeleteContractTerm(contractTermId || '');
    const isExistingTerm = !!contractTermId;

    useEffect(() => {
        if (contractTermId && fetchedTermData?.abacusContractTerm) {
            const fetchedTerm = fetchedTermData.abacusContractTerm;
            const mappedConditions: AbacusDistroContractTermCondition[] = (
                fetchedTerm.conditions || []
            ).map(condition => ({
                contractTermConditionId: condition.contractTermConditionId,
                contractTermConditionName:
                    condition.contractTermConditionName ?? null,
                contractTermId: condition.contractTermId,
                priority: condition.priority,
                termRate: String(condition.termRate),
                conditions: {
                    countries: condition.conditions?.countries ?? [],
                    stores: condition.conditions?.stores ?? [],
                    transactionTypes:
                        condition.conditions?.transactionTypes ?? [],
                },
            }));

            originalConditionIdsRef.current = mappedConditions
                .map(c => c.contractTermConditionId)
                .filter((id): id is string => Boolean(id));
            setEditableConditions(mappedConditions);

            setContractTermFormItem({
                contractId,
                contractTermId: fetchedTerm.contractTermId,
                vendor: fetchedTerm.attachmentsRelations?.labelIds?.[0]
                    ? {
                          id: {
                              vendorId: Number(
                                  fetchedTerm.attachmentsRelations.labelIds[0]
                              ),
                          },
                          name: fetchedTerm.attachmentsRelations.labelIds[0],
                      }
                    : undefined,
                attachments:
                    fetchedTerm.attachmentsObjects?.map(item => {
                        if (item.__typename === 'Isrc') {
                            return {
                                value: item.isrc,
                                name:
                                    item.globalSoundRecording?.name ??
                                    'Unknown Track',
                                artistName:
                                    item.globalSoundRecording
                                        ?.globalParticipants?.[0]?.name ??
                                    undefined,
                            };
                        }
                        if (item.__typename === 'Product') {
                            return {
                                value: item.upc ?? '',
                                name:
                                    item.productName ||
                                    item.upc ||
                                    'Unknown Product',
                                artistName:
                                    item.labelParticipations[0]
                                        ?.labelParticipant?.name || undefined,
                            };
                        }
                        if (item.__typename === 'UnknownProduct') {
                            return {
                                value: item.upc ?? '',
                                name: item.upc ? item.upc : 'Unknown Product',
                            };
                        }
                        if (item.__typename === 'NrContribution') {
                            return {
                                value:
                                    item.nrContributor.name ??
                                    'Unknown Contributor',
                                name:
                                    item.nrContributor.name ??
                                    'Unknown Contributor',
                            };
                        } else {
                            return {} as AttachmentDetail;
                        }
                    }) || [],
                contractTermName: fetchedTerm.contractTermName || null,
                termType: fetchedTerm.termType || undefined,
                attachmentsRelations: fetchedTerm.attachmentsRelations
                    ? {
                          labelIds:
                              fetchedTerm.attachmentsRelations.labelIds ?? null,
                          upcs: fetchedTerm.attachmentsRelations.upcs ?? null,
                      }
                    : null,
                conditions: mappedConditions,
                newConditions: [],
            });
        } else if (!contractTermId && !initialFormItem) {
            const defaultName = termType
                ? capitalize(termType.trim())
                : undefined;
            setContractTermFormItem({
                contractId,
                contractTermId: undefined,
                vendor: undefined,
                attachments: [],
                contractTermName: defaultName || null,
                termType: termType || undefined,
                attachmentsRelations: null,
                conditions: [],
                newConditions: [],
            });
            if (!defaultName) {
                setErrors(prev => ({
                    ...prev,
                    contractTermName: CONTRACT_TERMS_DISTRO_TERM_NAME_ERROR_MSG,
                }));
            }
        }
    }, [
        fetchedTermData,
        contractTermId,
        contractId,
        termType,
        initialFormItem,
    ]);

    useEffect(() => {
        const isTermNameValid = Boolean(
            contractTermFormItem.contractTermName &&
            contractTermFormItem.contractTermName.trim() !== '' &&
            !errors.contractTermName
        );

        const hasAnyErrors = Object.keys(errors).length > 0;

        const allConditionsHaveValidRates =
            editableConditions.length > 0 &&
            editableConditions.every(
                cond =>
                    cond.termRate &&
                    cond.termRate.trim() !== '' &&
                    CONTRACT_TERM_RATE.test(cond.termRate)
            );
        const shouldBeDisabled =
            !isTermNameValid ||
            hasAnyErrors ||
            fetchLoading ||
            !allConditionsHaveValidRates;
        setIsSaveDisabled(shouldBeDisabled);
    }, [
        contractTermFormItem.contractTermName,
        errors,
        fetchLoading,
        editableConditions,
    ]);

    const handleVendorChange = useCallback((vendor?: VendorSearchItem) => {
        setContractTermFormItem(prev => {
            const prevId = prev.vendor?.id.vendorId;
            const newId = vendor?.id.vendorId;
            const isChanged = prevId !== newId;
            return {
                ...prev,
                vendor,
                attachments: isChanged ? [] : prev.attachments,
                attachmentsRelations: vendor
                    ? {
                          labelIds: [String(vendor.id.vendorId)],
                          upcs: prev.attachmentsRelations?.upcs ?? null,
                      }
                    : null,
            };
        });
    }, []);

    const handleConditionsChange = (
        incoming: AbacusContractTermConditionInput[]
    ) => {
        const normalized: AbacusDistroContractTermCondition[] = incoming.map(
            (cond, index) => ({
                contractTermId: contractTermFormItem.contractTermId ?? '',
                contractTermConditionId:
                    (cond as AbacusDistroContractTermCondition)
                        .contractTermConditionId ?? '',
                contractTermConditionName:
                    (cond as AbacusDistroContractTermCondition)
                        .contractTermConditionName ?? null,
                priority: cond.priority ?? index + 1,
                termRate:
                    cond.termRate !== undefined && cond.termRate !== null
                        ? String(cond.termRate)
                        : null,
                conditions: {
                    countries: cond.conditions?.countries ?? [],
                    stores: cond.conditions?.stores ?? [],
                    transactionTypes: cond.conditions?.transactionTypes ?? [],
                },
            })
        );
        setEditableConditions(normalized);
    };

    const handleFormChangeValidation = (
        fieldName: keyof ContractTermFormItem,
        value: string | undefined
    ) => {
        if (fieldName === 'contractTermName') {
            if (!value || value.trim() === '') {
                setErrors(prev => ({
                    ...prev,
                    [fieldName]: CONTRACT_TERMS_DISTRO_TERM_NAME_ERROR_MSG,
                }));
            } else {
                setErrors(prev => {
                    const { [fieldName]: _, ...rest } = prev;
                    return rest;
                });
            }
        }
    };

    const handleFormChange = <K extends keyof ContractTermFormItem>(
        fieldName: K,
        value: ContractTermFormItem[K]
    ) => {
        setContractTermFormItem(prev => ({ ...prev, [fieldName]: value }));
        if (typeof value === 'string') {
            handleFormChangeValidation(fieldName, value);
        }
    };

    const handleAttachmentsChange = (attachments: string[]) => {
        const newAttachments: AttachmentDetail[] = attachments.map(
            attachment => ({
                value: attachment,
                name: attachment,
            })
        );
        setContractTermFormItem(prev => ({
            ...prev,
            attachments: newAttachments,
        }));
    };

    const handleSave = async () => {
        setIsSaveModalOpen(false);

        try {
            setIsSaveDisabled(true);

            if (
                !contractTermFormItem.contractTermName ||
                contractTermFormItem.contractTermName.trim() === ''
            ) {
                setErrors(prev => ({
                    ...prev,
                    contractTermName: CONTRACT_TERMS_DISTRO_TERM_NAME_ERROR_MSG,
                }));
                return;
            }

            const payloadTerm: ContractTermFormItem = {
                ...contractTermFormItem,
                conditions: editableConditions.map(cond => ({
                    contractTermConditionId: cond.contractTermConditionId,
                    contractTermConditionName: cond.contractTermConditionName,
                    contractTermId: cond.contractTermId,
                    priority: cond.priority,
                    termRate: cond.termRate,
                    conditions: cond.conditions,
                })),
                newConditions: [],
            };

            const termIdToUse =
                contractTermId || contractTermFormItem.contractTermId;
            let finalTermId = termIdToUse;

            if (!finalTermId) {
                const newId = await saveHandler({ contractTerm: payloadTerm });
                if (!newId) {
                    setErrors(prev => ({
                        ...prev,
                        server: 'Failed to retrieve new contract term ID.',
                    }));
                    return;
                }
                finalTermId = newId;
                setCreatedContractTermId(newId);
            } else {
                await saveHandler({ contractTerm: payloadTerm });
            }

            const currentIds = editableConditions
                .map(c => c.contractTermConditionId)
                .filter((id): id is string => Boolean(id));
            const toDelete = originalConditionIdsRef.current.filter(
                id => !currentIds.includes(id)
            );
            if (toDelete.length) {
                await deleteContractTermConditions({
                    variables: { contractTermConditionId: toDelete },
                });
            }

            const payloadConditions = editableConditions.map(cond => ({
                contractTermConditionId: cond.contractTermConditionId ?? null,
                priority: cond.priority ?? 0,
                termRate: cond.termRate ? parseFloat(cond.termRate) : 0,
                conditions: cond.conditions,
            }));

            if (finalTermId && payloadConditions.length) {
                await saveConditions({
                    contractTermId: finalTermId,
                    conditionsPayload: payloadConditions,
                });
            }
            currentIds.length
                ? toast('The term has been successfully updated.')
                : toast(
                      'The term has been successfully added to the contract.'
                  );
            history.push(getContractDetail(contractId));
        } catch (e) {
            let errorMessage = 'Failed to create/update contract term.';
            if (e instanceof Error) {
                errorMessage = `Error: ${e.message} ${errorMessage}`;
            } else if (Array.isArray(e)) {
                errorMessage = `Error: ${JSON.stringify(e[0])} ${errorMessage}`;
            } else {
                errorMessage = `Error: ${String(e)} ${errorMessage}`;
            }
            setServerError(errorMessage);
            setIsSaveDisabled(false);
        } finally {
            setIsSaveDisabled(false);
        }
    };

    const handleDeleteContractTerm = async () => {
        setIsDeleteModalOpen(false);
        const deleteVariables = {
            contractTermId: contractTermFormItem.contractTermId!,
        };
        await deleteContractTerm({
            variables: deleteVariables,
        });
        toast('The term has been successfully deleted.');
        history.push(getContractDetail(contractId));
    };

    return (
        <Page>
            <PageHeader breadcrumbs={breadcrumbs}>
                <>
                    <div className="ContractTermsDistroFormFields-Header">
                        <PageHeader.Title>
                            {pageTitle}
                            {isExistingTerm && (
                                <Button
                                    className="ContractTermsDistroFormFields-Header-Button ml-auto"
                                    variant="secondary"
                                    onClick={() => setIsDeleteModalOpen(true)}
                                    data-testid="contractFormFieldsDeleteButton"
                                >
                                    Delete Term
                                </Button>
                            )}
                        </PageHeader.Title>
                        {isExistingTerm && (
                            <span>
                                <strong>ID:</strong>
                                {contractTermId}
                            </span>
                        )}
                        {(serverError || Object.keys(errors).length > 0) && (
                            <Alert
                                variant="error"
                                text={
                                    <ErrorMessages
                                        serverError={serverError ?? null}
                                        errors={errors}
                                    />
                                }
                            />
                        )}
                    </div>
                </>
            </PageHeader>
            <Page.Body>
                <Section>
                    <Form.Row className="ContractTermsDistroFormFields-FormRow">
                        <ContractTermName
                            contractTermName={
                                contractTermFormItem.contractTermName ??
                                undefined
                            }
                            handleFormChange={handleFormChange}
                            handleFormChangeValidation={
                                handleFormChangeValidation
                            }
                            errors={errors}
                        />
                        <VendorSearch
                            errors={errors}
                            setErrors={setErrors}
                            initialVendorId={
                                contractTermFormItem.vendor?.id.vendorId
                            }
                            onChange={handleVendorChange}
                        />
                    </Form.Row>

                    <Form.Row className="ContractTermsDistroFormFields-FormRow-Attachments">
                        <ContractTermsDistroAttachmentsSelect
                            labelId={contractTermFormItem.vendor?.id.vendorId}
                            errors={errors}
                            onOptionsChange={handleAttachmentsChange}
                            initialOptions={contractTermFormItem.attachments}
                            termType={termType}
                        />
                    </Form.Row>

                    <Form.Row>
                        <ContractTermsDistroConditionsList
                            termId={
                                contractTermFormItem.contractTermId || undefined
                            }
                            contractTermConditions={editableConditions}
                            onConditionsChange={handleConditionsChange}
                            isLoading={fetchLoading}
                            setIsSaveDisabled={setIsSaveDisabled}
                            setErrors={setErrors}
                        />
                    </Form.Row>

                    <Form.Row className="ContractTermsDistroFormFields-FormRow-Buttons SuiteButtonOverrides">
                        <Button
                            className="ContractTermsDistroFormFields-Button"
                            variant="secondary"
                            onClick={() =>
                                history.push(getContractDetail(contractId))
                            }
                            data-testid="contractFormFieldsCancelButton"
                        >
                            Cancel
                        </Button>
                        <Button
                            className="ContractTermsDistroFormFields-Button"
                            variant="primary"
                            onClick={
                                contractTermFormItem.attachments?.length
                                    ? handleSave
                                    : () => setIsSaveModalOpen(true)
                            }
                            disabled={isSaveDisabled || fetchLoading}
                            data-testid="contractFormFieldsSaveButton"
                        >
                            Save
                        </Button>
                    </Form.Row>
                </Section>
            </Page.Body>
            <Modal
                className="ContractTermsDistroFormFields SuiteButtonOverrides"
                title="Are you sure you want to save?"
                isOpen={isSaveModalOpen}
                onRequestClose={() => setIsSaveModalOpen(false)}
                onConfirm={handleSave}
                confirmTitle="Save"
            >
                {`No ${termType}s have been added to this term. Are you sure you want to save without them?`}
            </Modal>
            <Modal
                title="Delete Term"
                isOpen={isDeleteModalOpen}
                onRequestClose={() => setIsDeleteModalOpen(false)}
                onConfirm={handleDeleteContractTerm}
                confirmTitle="Delete"
            >
                Are you sure you want to delete this term?
                <br />
                This action cannot be undone.
            </Modal>
        </Page>
    );
};
