import { ApolloClient } from '@apollo/client';
import abacusUpdateContractTermAttachments from 'src/apollo/mutations/contract-term/update-contract-term-attachments.gql';
import { getContractTerm } from 'src/apollo/queries/contract-term';
import {
    BulkEditEntryBase,
    BulkSchema,
    runInBatches,
} from './common-bulk-edit-schema';
import type { GridTableColumnDefinition } from '@theorchard/suite-components';
import React from 'react';
import { Link } from 'react-router-dom';
import { getContractDetail } from 'src/urls/frontend-royalties';
import {
    GET_CONTRACT_TERM_MAX_PARALLEL,
    UPDATE_CONTRACT_TERM_MAX_PARALLEL,
} from 'src/components/contract-bulk-edit/common/edit-schema/constants';

const OVERFLOW_WRAP_STYLE = {
    whiteSpace: 'normal' as const,
    overflowWrap: 'anywhere' as const,
};

const AttachmentsCell: React.FC<{
    attachments: string[] | undefined | null;
}> = ({ attachments }) => (
    <div style={OVERFLOW_WRAP_STYLE}>
        {attachments && attachments.length > 0
            ? `${attachments.length} items: ${attachments.join(', ')}`
            : '-'}
    </div>
);

const ATTACHMENTS_COLUMN_MIN_WIDTH = '1fr';

const CONTRACT_TERM_TABLE_HEADERS: GridTableColumnDefinition<ContractTermBulkEditEntry>[] =
    [
        {
            name: 'contractTermId',
            title: 'Contract Term Id',
        },
        {
            name: 'contractTermName',
            title: 'Contract Term Name',
            Cell: ({ data: { contractTermName } }: any) => (
                <div style={OVERFLOW_WRAP_STYLE}>{contractTermName ?? '-'}</div>
            ),
        },
        {
            name: 'contractName',
            title: 'Contract Name',
            Cell: ({ data: { contractId, contractName } }: any) => (
                <div style={OVERFLOW_WRAP_STYLE}>
                    {contractName ? (
                        <Link
                            key={contractId}
                            to={getContractDetail(contractId)}
                        >
                            {contractName}
                        </Link>
                    ) : (
                        '-'
                    )}
                </div>
            ),
        },
        {
            name: 'currentAttachments',
            title: 'Current Attachments',
            minWidth: ATTACHMENTS_COLUMN_MIN_WIDTH,
            Cell: ({ data: { currentAttachments } }: any) => (
                <AttachmentsCell attachments={currentAttachments} />
            ),
        },
        {
            name: 'attachmentsToAppend',
            title: 'Attachments to Append',
            minWidth: ATTACHMENTS_COLUMN_MIN_WIDTH,
            Cell: ({ data: { attachmentsToAppend } }: any) => (
                <AttachmentsCell attachments={attachmentsToAppend} />
            ),
        },
        {
            name: 'attachmentsToSet',
            title: 'Attachments to Set',
            minWidth: ATTACHMENTS_COLUMN_MIN_WIDTH,
            Cell: ({ data: { attachmentsToSet } }: any) => (
                <AttachmentsCell attachments={attachmentsToSet} />
            ),
        },
        {
            name: 'calculatedAttachments',
            title: 'Calculated Attachments',
            minWidth: ATTACHMENTS_COLUMN_MIN_WIDTH,
            Cell: ({ data: { calculatedAttachments } }: any) => (
                <AttachmentsCell attachments={calculatedAttachments} />
            ),
        },
    ];

export interface ContractTermBulkEditEntry extends BulkEditEntryBase {
    id: string;
    contractTermId: string;
    contractTermName: string | undefined | null;
    currentAttachments: string[] | undefined | null;
    contractName: string | undefined | null;
    contractId: number | undefined | null;

    attachmentsToAppend: string[] | undefined | null;
    attachmentsToSet: string[] | undefined | null;
    calculatedAttachments: string[] | undefined | null;
}

export const contractTermsSchema: BulkSchema<ContractTermBulkEditEntry> = {
    id: 'contractTerms',
    displayName: 'Contract Terms',
    columns: CONTRACT_TERM_TABLE_HEADERS,
    expectedFileColumns: [
        {
            name: 'contract_term_id',
            description: 'Contract Term ID to update',
        },
        {
            name: 'attachments.append',
            description:
                'Comma-separated list of attachments to append. UPC/ISRC.',
        },
        {
            name: 'attachments.set',
            description:
                'Comma-separated list of attachments to set. UPC/ISRC. Has higher priority than attachments.append.',
        },
    ],
    async toEntries(apolloClient: ApolloClient<object>, rows: any[]) {
        async function mapJsonToEditEntries(
            parsedJsonArray: any[]
        ): Promise<ContractTermBulkEditEntry[]> {
            const parseAttachments = (value: any): string[] => {
                if (value === undefined || value === null || value === '') {
                    return [];
                }
                return String(value)
                    .split(',')
                    .map((v: string) => v.trim())
                    .filter((v: string) => v.length > 0);
            };

            const mapEntry = async (
                entry: any
            ): Promise<ContractTermBulkEditEntry> => {
                const contractTermId = entry['contract_term_id'];

                const attachmentsToAppend = parseAttachments(
                    entry['attachments.append']
                );
                const attachmentsToSet = parseAttachments(
                    entry['attachments.set']
                );

                const { data: contractTermData } = await apolloClient.query({
                    query: getContractTerm,
                    variables: { contractTermId },
                    fetchPolicy: 'network-only',
                });

                if (!contractTermData.abacusContractTerm) {
                    return {
                        id: contractTermId,
                        contractTermId,
                        attachmentsToAppend,
                        attachmentsToSet,
                        calculatedAttachments: null,
                        contractTermName: null,
                        currentAttachments: null,
                        contractName: null,
                        contractId: null,
                    };
                }

                const calculatedAttachments =
                    attachmentsToSet.length > 0
                        ? attachmentsToSet
                        : Array.from(
                              new Set([
                                  ...(contractTermData.abacusContractTerm
                                      .attachments || []),
                                  ...attachmentsToAppend,
                              ])
                          );

                return {
                    id: contractTermId,
                    contractTermId,
                    attachmentsToAppend,
                    attachmentsToSet,
                    calculatedAttachments,
                    contractTermName:
                        contractTermData.abacusContractTerm.contractTermName,
                    currentAttachments:
                        contractTermData.abacusContractTerm.attachments,
                    contractName:
                        contractTermData.abacusContractTerm.contract
                            .contractName,
                    contractId:
                        contractTermData.abacusContractTerm.contract.contractId,
                };
            };

            return await runInBatches(
                parsedJsonArray,
                GET_CONTRACT_TERM_MAX_PARALLEL,
                mapEntry
            );
        }

        return await mapJsonToEditEntries(rows);
    },
    async apply(apolloClient, entries, onSuccess, onFail) {
        await runInBatches(
            entries,
            UPDATE_CONTRACT_TERM_MAX_PARALLEL,
            async entry => {
                if (!entry.calculatedAttachments) {
                    onFail(
                        entry,
                        new Error('No attachments to update'),
                        'Term not found.'
                    );
                    return;
                }

                const variables: any = {
                    attachments: entry.calculatedAttachments,
                    contractTermId: entry.contractTermId,
                };

                try {
                    await apolloClient.mutate({
                        mutation: abacusUpdateContractTermAttachments,
                        variables,
                    });
                    onSuccess(entry);
                } catch (error) {
                    onFail(entry, error, 'Error updating attachments.');
                }
            }
        );
    },
};
