import { jsonToGraphQLQuery } from 'json-to-graphql-query';
import { Data } from '../../../types';
import { BulkEditConfig } from './BulkEdit.model';

export const generateBulkEditMutation = <T extends Data>(
  config: BulkEditConfig,
  values: T,
  filter?: object,
): string => {
  const { mutation, fields, keys } = config;

  const relatedEntitiesToAddKey = keys?.add ?? 'relatedEntitiesToAdd';
  const relatedEntitiesToRemoveKey = keys?.remove ?? 'relatedEntitiesToRemove';
  const relatedEntitiesToClearKey = keys?.clear ?? 'relatedEntitiesToClear';
  const setKey = keys?.set ?? 'set';
  const filterKey = keys?.filter ?? 'filter';

  const relatedEntitiesToAdd = Object.entries(fields)
    .filter(([_, field]) => field.action === relatedEntitiesToAddKey)
    .reduce((acc, [key, field]) => {
      const fieldValues = values[key];

      if (fieldValues) {
        if (Array.isArray(fieldValues)) {
          return {
            ...acc,
            [field.originalFieldName]: [
              ...(Array.isArray(acc[field.originalFieldName])
                ? (acc[field.originalFieldName] as unknown[])
                : []),
              ...fieldValues.map((item: string | object) => {
                if (typeof item === 'object') {
                  return { ...item };
                }
                if (
                  Array.isArray(field.type) &&
                  field.type.length > 0 &&
                  field.type[0] &&
                  typeof field.type[0] === 'object' &&
                  Object.keys(field.type[0]).length > 0
                ) {
                  const key = Object.keys(field.type[0])[0];
                  return {
                    [key]: item,
                  };
                } else {
                  throw new Error(
                    `Invalid field type. Check generated bulk edit configuration. Generating graphql mutation for field: ${JSON.stringify(
                      field,
                    )}`,
                  );
                }
              }),
            ],
          };
        } else {
          throw new Error(
            `Assignment field is not an array. Generating graphql mutation for field: ${JSON.stringify(
              field,
            )}`,
          );
        }
      }
      return acc;
    }, {} as Record<string, unknown>);

  const relatedEntitiesToRemove = Object.entries(fields)
    .filter(([_, field]) => field.action === relatedEntitiesToRemoveKey)
    .reduce((acc, [key, field]) => {
      const fieldValues = values[key];

      if (fieldValues) {
        if (fieldValues === field.clearValue) {
          return acc; // Skip adding to remove if clearValue is selected
        }

        if (Array.isArray(fieldValues)) {
          return {
            ...acc,
            [field.originalFieldName]: [
              ...(Array.isArray(acc[field.originalFieldName])
                ? (acc[field.originalFieldName] as unknown[])
                : []),
              ...fieldValues.map((item: string | object) => {
                if (typeof item === 'object') {
                  return { ...item };
                }
                if (
                  Array.isArray(field.type) &&
                  field.type.length > 0 &&
                  field.type[0] &&
                  typeof field.type[0] === 'object' &&
                  Object.keys(field.type[0]).length > 0
                ) {
                  const key = Object.keys(field.type[0])[0];
                  return {
                    [key]: item,
                  };
                } else {
                  throw new Error(
                    `Invalid field type. Check generated bulk edit configuration. Generating graphql mutation for field: ${JSON.stringify(
                      field,
                    )}`,
                  );
                }
              }),
            ],
          };
        } else {
          throw new Error(
            `Assignment field is not an array. Generating graphql mutation for field: ${JSON.stringify(
              field,
            )}`,
          );
        }
      }
      return acc;
    }, {} as Record<string, unknown>);

  const relatedEntitiesToClear = Object.entries(fields)
    .filter(([_, field]) => field.action === relatedEntitiesToRemoveKey)
    .reduce((acc, [key, field]) => {
      const fieldValues = values[key];

      if (fieldValues && field.clearValue === fieldValues) {
        acc = {
          ...acc,
          [field.originalFieldName]: true,
        };
      }
      return acc;
    }, {} as Record<string, unknown>);

  const set = Object.entries(fields)
    .filter(([_, field]) => field.action === setKey)
    .reduce((acc, [key, field]) => {
      const fieldValues = values[key];

      if (fieldValues !== undefined && fieldValues !== null) {
        acc = {
          ...acc,
          [field.originalFieldName]: fieldValues,
        };
      }
      return acc;
    }, {} as Record<string, unknown>);

  if (
    Object.keys(relatedEntitiesToAdd).length === 0 &&
    Object.keys(relatedEntitiesToRemove).length === 0 &&
    Object.keys(relatedEntitiesToClear).length === 0 &&
    Object.keys(set).length === 0
  ) {
    throw new Error('No valid fields to generate mutation');
  }

  return jsonToGraphQLQuery({
    mutation: {
      [mutation]: {
        __args: {
          ...(Object.keys(relatedEntitiesToAdd).length > 0 && {
            [relatedEntitiesToAddKey]: relatedEntitiesToAdd,
          }),
          ...(Object.keys(relatedEntitiesToRemove).length > 0 && {
            [relatedEntitiesToRemoveKey]: relatedEntitiesToRemove,
          }),
          ...(Object.keys(relatedEntitiesToClear).length > 0 && {
            [relatedEntitiesToClearKey]: relatedEntitiesToClear,
          }),
          ...(Object.keys(set).length > 0 && {
            [setKey]: set,
          }),
          ...(filter && { [filterKey]: filter }),
        },
        filterMatchedIds: true,
      },
    },
  });
};
