import type { ReactNode } from 'react';
import { useCallback, useMemo, useState } from 'react';
import type { ProFormInstance } from '@ant-design/pro-form';
import {
  ProFormText,
  ProFormDigit,
  ProFormSelect,
  ProFormCheckbox,
  ProFormDatePicker,
  ProFormDateTimePicker,
} from '@ant-design/pro-form';
import type { ProFormFieldItemProps } from '@ant-design/pro-form/lib/interface';
import { graphql } from '@/services/mgr/api';
import { getEnumSelectOptions } from '../utils/tools';
import type { NamePath } from 'antd/lib/form/interface';
import debounce from 'lodash/debounce';

function getProFieldSetting(field: Api.SingleTableField): ProFormFieldItemProps {
  return {
    name: field.code.split('.'),
    rules: [
      {
        required: field.required,
        message: `请输入${field.name}`,
      },
    ],
    label: field.name,
    width: 'md',
  };
}

interface FieldBuilder {
  readonly build: (
    field: Api.SingleTableField,
    singleTable: Api.SingleTable,
    appendExValues: (ev: any) => void,
    form: React.MutableRefObject<ProFormInstance<any> | undefined>,
  ) => ReactNode[];
}

class DefaultBuilder implements FieldBuilder {
  readonly build = (field: Api.SingleTableField) => {
    return [<ProFormText key={field.code} {...getProFieldSetting(field)} />];
  };
}

class TextBuilder implements FieldBuilder {
  readonly build = (field: Api.SingleTableField) => {
    return [<ProFormText key={field.code} {...getProFieldSetting(field)} />];
  };
}

class BooleanSelectBuilder implements FieldBuilder {
  readonly build = (field: Api.SingleTableField) => {
    return [<ProFormCheckbox key={field.code} {...getProFieldSetting(field)} />];
  };
}

class IdEntityBuilder implements FieldBuilder {
  readonly build = (field: Api.SingleTableField) => {
    return [<ProFormText key={field.code} {...getProFieldSetting(field)} />];
  };
}

function getSubFieldSpecification(
  sfVar: Api.SingleTableField | undefined,
  getFieldValue: (name: NamePath) => any,
) {
  if (sfVar) {
    const path = sfVar.code.split('.');
    const name = path.slice(-1).pop();
    const val = getFieldValue(path);
    if (!!!val) {
      return '';
    }
    if (typeof val === 'string') {
      if (val.length === 0) {
        return '';
      }
    }
    if (path.length > 2) {
      let s = `${name}:{_v: eq,_val: "${val}"}`;
      for (let i = path.length - 2; i > 0; i--) {
        s = `${path[i]}:{${s}}`;
      }
      return s;
    } else {
      return `${name}:{_v: eq,_val: "${val}"}`;
    }
  }
  return '';
}

const KeysEntity: React.FC<{
  field: Api.SingleTableField;
  singleTable: Api.SingleTable;
  appendExValues: (ev: any) => void;
  form: React.MutableRefObject<ProFormInstance<any> | undefined>;
}> = ({ field, singleTable, appendExValues, form }) => {
  const [validateStatus, setValidateStatus] = useState<
    'success' | 'warning' | 'error' | 'validating' | undefined
  >();
  const [help, sethelp] = useState<string | undefined>();

  const subFields = useMemo(
    () =>
      field.keys
        .split(',')
        .map((subCode) =>
          singleTable.fields.filter((f) => f.code === `${field.code}.${subCode}`).pop(),
        )
        .filter((item) => item) as Api.SingleTableField[],
    [field, singleTable],
  );

  const appendEntityId = useCallback(
    (id: any) => {
      const fieldVal: any = new Object();
      fieldVal[field.code] = id;
      appendExValues(fieldVal);
    },
    [appendExValues, field],
  );

  const keysEntityValidator = useCallback(
    async (getFieldValue: (name: NamePath) => any) => {
      const subSpecificationList = subFields.map((sfVar) => {
        return getSubFieldSpecification(sfVar, getFieldValue);
      });
      appendEntityId(undefined);

      const emptyCount = subSpecificationList.filter((i) => i.length <= 0).length;
      const hasValueCount = subSpecificationList.filter((i) => i.length > 0).length;

      if (hasValueCount <= 0) {
        setValidateStatus(undefined);
        sethelp(undefined);
        return;
      }

      if (emptyCount > 0) {
        setValidateStatus('error');
        sethelp(`${field.name}不存在`);
        return;
      }

      const specification = subSpecificationList.join(',');

      if (specification.length === 0) {
        return;
      }
      const gqlString = `query{${field.dataType.substring(
        1,
      )}(specification:{${specification}}){id}}`;
      const graphqlParams = {
        operationName: null,
        variables: {},
        query: gqlString,
      };
      const reqData = await graphql(graphqlParams);
      const data = reqData.data[field.dataType.substring(1)] as { id: string }[];
      if (data && data.length > 0) {
        appendEntityId(data[0].id);
        setValidateStatus(undefined);
        sethelp(undefined);
        return;
      } else {
        setValidateStatus('error');
        sethelp(`${field.name}不存在`);
        return;
      }
    },
    [field, subFields, appendEntityId],
  );

  const debounced = debounce(keysEntityValidator, 100);

  return (
    <>
      {subFields.map((subField) => {
        const props = getProFieldSetting(subField);
        return (
          <ProFormText
            key={subField.code}
            {...props}
            validateStatus={validateStatus}
            help={help}
            fieldProps={{
              onChange: async () => {
                await debounced(form?.current?.getFieldValue || (() => undefined));
              },
            }}
            rules={[...(props.rules ? props.rules : [])]}
          />
        );
      })}
    </>
  );
};

class KeysEntityBuilder implements FieldBuilder {
  readonly build = (
    field: Api.SingleTableField,
    singleTable: Api.SingleTable,
    appendExValues: (ev: any) => void,
    form: React.MutableRefObject<ProFormInstance<any> | undefined>,
  ) => {
    return [
      <KeysEntity
        key={field.code}
        field={field}
        singleTable={singleTable}
        appendExValues={appendExValues}
        form={form}
      />,
    ];
  };
}
class SelectEntityBuilder implements FieldBuilder {
  readonly build = (
    field: Api.SingleTableField,
    singleTable: Api.SingleTable,
    appendExValues: (ev: any) => void,
  ) => {
    const props = getProFieldSetting(field);
    return [
      <ProFormSelect
        key={field.code + 'id'}
        {...props}
        name={[...field.code.split('.'), 'id']}
        params={{
          singleTable: singleTable,
          field: field,
        }}
        request={async (params) => {
          return await getEnumSelectOptions(
            params.field.dataType.substring(1),
            params.field.keys,
            params.field.display,
          );
        }}
        rules={[
          ...(props.rules ? props.rules : []),
          {
            async validator(_, value) {
              if (!!!value || value.trim() === '') {
                return;
              }
              const fieldVal: any = new Object();
              fieldVal[field.code] = value;
              appendExValues(fieldVal);
            },
          },
        ]}
      />,
    ];
  };
}
class IdEnumBuilder implements FieldBuilder {
  readonly build = (field: Api.SingleTableField) => {
    return [<ProFormText key={field.code} {...getProFieldSetting(field)} />];
  };
}
class KeysEnumBuilder implements FieldBuilder {
  readonly build = (
    field: Api.SingleTableField,
    singleTable: Api.SingleTable,
    appendExValues: (ev: any) => void,
  ) => {
    const props = getProFieldSetting(field);
    return [
      <ProFormSelect
        key={field.code + 'id'}
        {...props}
        name={[...field.code.split('.'), 'id']}
        params={{
          singleTable: singleTable,
          field: field,
        }}
        request={async (params) => {
          return await getEnumSelectOptions(
            params.field.dataType.substring(1),
            params.field.keys,
            params.field.display,
          );
        }}
        rules={[
          ...(props.rules ? props.rules : []),
          {
            async validator(_, value) {
              if (!!!value) {
                return;
              }
              const fieldVal: any = new Object();
              fieldVal[field.code] = value;
              appendExValues(fieldVal);
            },
          },
        ]}
      />,
    ];
  };
}

class DateRangeBuilder implements FieldBuilder {
  readonly build = (field: Api.SingleTableField) => {
    return [<ProFormDatePicker key={field.code} {...getProFieldSetting(field)} />];
  };
}

class DateTimeRangeBuilder implements FieldBuilder {
  readonly build = (field: Api.SingleTableField) => {
    return [<ProFormDateTimePicker key={field.code} {...getProFieldSetting(field)} />];
  };
}

class DigitIntegerBuilder implements FieldBuilder {
  readonly build = (field: Api.SingleTableField) => {
    return [
      <ProFormDigit
        key={field.code}
        {...getProFieldSetting(field)}
        fieldProps={{ precision: 0 }}
      />,
    ];
  };
}

class DigitLongBuilder implements FieldBuilder {
  readonly build = (field: Api.SingleTableField) => {
    return [
      <ProFormDigit
        key={field.code}
        {...getProFieldSetting(field)}
        fieldProps={{ precision: 0 }}
      />,
    ];
  };
}

class DigitDecimalBuilder implements FieldBuilder {
  readonly build = (field: Api.SingleTableField) => {
    return [
      <ProFormDigit
        key={field.code}
        {...getProFieldSetting(field)}
        fieldProps={{ precision: 4 }}
      />,
    ];
  };
}

class FieldBuilderManager {
  readonly map: Map<string, FieldBuilder> = new Map([
    ['default', new DefaultBuilder()],
    ['text.string', new TextBuilder()],
    ['text.id', new TextBuilder()],
    ['select.boolean', new BooleanSelectBuilder()],
    ['id.entity', new IdEntityBuilder()],
    ['keys.entity', new KeysEntityBuilder()],
    ['select.entity', new SelectEntityBuilder()],
    ['id.enum', new IdEnumBuilder()],
    ['keys.enum', new KeysEnumBuilder()],
    ['dateRange.date', new DateRangeBuilder()],
    ['dateTimeRange.datetime', new DateTimeRangeBuilder()],
    ['digit.integer', new DigitIntegerBuilder()],
    ['digit.long', new DigitLongBuilder()],
    ['digit.decimal', new DigitDecimalBuilder()],
  ]);
  readonly defaultBuilder = new DefaultBuilder();

  build(
    mode: 'create' | 'edit',
    singleTable: Api.SingleTable,
    appendExValues: (ev: any) => void,
    form: React.MutableRefObject<ProFormInstance<any> | undefined>,
  ) {
    const fieldNodes: ReactNode[] = [];
    let fields: Api.SingleTableField[];
    if (mode === 'edit') {
      fields = singleTable.fields
        .filter((f) => !!!f.hideInEditForm)
        .filter((f) => f.code.split('.').length <= 1);
    } else {
      fields = singleTable.fields
        .filter((f) => !!!f.hideInCreateForm)
        .filter((f) => f.code.split('.').length <= 1);
    }
    fields.forEach((f) => {
      fieldNodes.push(this.buildField(f, singleTable, appendExValues, form));
    });
    return fieldNodes;
  }

  private buildField(
    field: Api.SingleTableField,
    singleTable: Api.SingleTable,
    appendExValues: (ev: any) => void,
    form: React.MutableRefObject<ProFormInstance<any> | undefined>,
  ) {
    const builder = this.map.get(field.valueType);
    if (builder) {
      return builder.build(field, singleTable, appendExValues, form);
    }
    return this.defaultBuilder.build(field);
  }
}

const fieldBuilderManager = new FieldBuilderManager();

export default fieldBuilderManager;
