/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *    https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
import { RenderCallbackParams } from '/@/components/Form/src/types/form';
import { Icon, SvgIcon } from '/@/components/Icon';
import options from '../data/option';

import Mergely from '../components/Mergely.vue';
import CustomForm from '../components/CustomForm';
import {
  Alert,
  Dropdown,
  Form,
  Input,
  InputNumber,
  Menu,
  Select,
  Switch,
  Tag,
} from 'ant-design-vue';
import { Button } from '/@/components/Button';
import { descriptionFilter } from '../utils';
import { SettingTwoTone } from '@ant-design/icons-vue';
import { ref, unref } from 'vue';
import { handleConfTemplate } from '/@/api/flink/config';
import { decodeByBase64 } from '/@/utils/cipher';
import { useMessage } from '/@/hooks/web/useMessage';
import { SelectValue } from 'ant-design-vue/lib/select';
import { CandidateTypeEnum, FailoverStrategyEnum, RestoreModeEnum } from '/@/enums/flinkEnum';
import { useI18n } from '/@/hooks/web/useI18n';
import { fetchYarnQueueList } from '/@/api/setting/yarnQueue';
import { ApiSelect } from '/@/components/Form';
import { ResourceTypeEnum } from '/@/views/resource/upload/upload.data';

const { t } = useI18n();
/* render input dropdown component */
export const renderInputDropdown = (
  formModel: Recordable,
  field: string,
  componentProps: { placeholder: string; options: Array<string> },
) => {
  return (
    <Input
      type="text"
      placeholder={componentProps.placeholder}
      allowClear
      value={formModel[field]}
      onInput={(e) => (formModel[field] = e.target.value)}
    >
      {{
        addonAfter: () => (
          <Dropdown placement="bottomRight">
            {{
              overlay: () => (
                <div>
                  <Menu trigger="['click', 'hover']">
                    {componentProps.options.map((item) => {
                      return (
                        <Menu.Item
                          key={item}
                          onClick={() => (formModel[field] = item)}
                          class="pr-60px"
                        >
                          <Icon icon="ant-design:plus-circle-outlined" />
                          {{ item }}
                        </Menu.Item>
                      );
                    })}
                  </Menu>
                  <Icon icon="ant-design:history-outlined" />
                </div>
              ),
            }}
          </Dropdown>
        ),
      }}
    </Input>
  );
};

export function handleCheckCheckpoint(values: Recordable) {
  const { cpMaxFailureInterval, cpFailureRateInterval, cpFailureAction } = values.checkPointFailure;
  if (cpMaxFailureInterval != null && cpFailureRateInterval != null && cpFailureAction != null) {
    if (cpFailureAction === FailoverStrategyEnum.ALERT) {
      if (values.alertId == null) {
        // this.form.setFields({
        //   alertEmail: {
        //     errors: [new Error('checkPoint failure trigger is alert,email must not be empty')],
        //   },
        // });
        return Promise.reject('trigger action is alert,Fault Alert Template must not be empty');
      } else {
        return Promise.resolve();
      }
    } else {
      return Promise.resolve();
    }
  } else if (
    cpMaxFailureInterval == null &&
    cpFailureRateInterval == null &&
    cpFailureAction == null
  ) {
    return Promise.resolve();
  } else {
    return Promise.reject('options all required or all empty');
  }
}

/* render input Group component */
export const renderInputGroup = ({ model }) => {
  if (!Reflect.has(model, 'checkPointFailure')) {
    model.checkPointFailure = {};
  }
  const handleUpdate = (value: any) => {
    Object.assign(model, {
      checkPointFailure: value,
    });
  };
  return (
    <Form.Item
      label={t('flink.app.noteInfo.checkPointFailureOptions')}
      name="checkPointFailure"
      rules={[{ validator: () => handleCheckCheckpoint(model) }]}
    >
      <CustomForm
        value={{
          cpMaxFailureInterval: model.checkPointFailure.cpMaxFailureInterval,
          cpFailureRateInterval: model.checkPointFailure.cpFailureRateInterval,
          cpFailureAction: model.checkPointFailure.cpFailureAction,
        }}
        onUpdateValue={handleUpdate}
      ></CustomForm>
    </Form.Item>
  );
};
/*Gets the selection of totalOptions */
function getTotalOptions() {
  return [
    {
      name: 'process memory(进程总内存)',
      options: options.filter((x) => x.group === 'process-memory'),
    },
    {
      name: 'total memory(Flink 总内存)',
      options: options.filter((x) => x.group === 'total-memory'),
    },
  ];
}
/* render total memory component */
export const renderTotalMemory = ({ model, field }: RenderCallbackParams) => {
  return (
    <div>
      <Select
        show-search
        allow-clear
        mode="multiple"
        max-tag-count={2}
        options={getTotalOptions()}
        field-names={{ label: 'name', value: 'key', options: 'options' }}
        value={model[field]}
        onChange={(value) => (model[field] = value)}
        placeholder="Please select the resource parameters to set"
      />
      <p class="conf-desc mt-10px">
        <span class="note-info">
          <Tag color="#2db7f5" class="tag-note">
            {t('flink.app.noteInfo.note')}
          </Tag>
          <span>{t('flink.app.noteInfo.totalMemoryNote')}</span>
        </span>
      </p>
    </div>
  );
};

function hasOptions(items) {
  return options.filter((x) => items.includes(x.key)) as any;
}
/* render memory option */
export const renderOptionsItems = (
  model: Recordable,
  parentField: string,
  field: string,
  reg: string,
  replace = false,
) => {
  return hasOptions(model[parentField] || []).map((conf: Recordable) => {
    let label = conf.name.replace(new RegExp(reg, 'g'), '');
    if (replace) label = label.replace(/\./g, ' ');
    if (!Reflect.has(model, field)) {
      model[field] = {};
    }
    if (!model[field][conf.key]) {
      model[field][conf.key] = conf.defaultValue || null;
    }
    if (!Reflect.has(model, conf.key)) {
      model[conf.key] = model[field][conf.key] || conf.defaultValue || null;
    }

    function handleValueChange(value: string) {
      model[conf.key] = value;
      model[field][conf.key] = value;
    }
    return (
      <Form.Item label={label} name={conf.key} key={`${field}.${conf.key}`}>
        {conf.type === 'number' && (
          <InputNumber
            class="!w-full"
            min={conf.min}
            max={conf.max}
            step={conf.step}
            value={model[field][conf.key]}
            onChange={handleValueChange}
            rules={[{ validator: conf.validator }]}
          />
        )}
        {conf.type === 'switch' && <span class="tip-info">({conf.placeholder})</span>}
        <p class="conf-desc"> {descriptionFilter(conf)} </p>
      </Form.Item>
    );
  });
};

/* render Yarn Queue */
export const renderYarnQueue = ({ model, field }: RenderCallbackParams) => {
  return (
    <div>
      <ApiSelect
        name="yarnQueue"
        placeholder={t('setting.yarnQueue.placeholder.yarnQueueLabelExpression')}
        api={fetchYarnQueueList}
        params={{ page: 1, pageSize: 9999 }}
        resultField={'records'}
        labelField={'queueLabel'}
        valueField={'queueLabel'}
        showSearch={true}
        value={model[field]}
        onChange={(value: string) => (model[field] = value)}
      />
      <p class="conf-desc mt-10px">
        <span class="note-info">
          <Tag color="#2db7f5" class="tag-note">
            {t('flink.app.noteInfo.note')}
          </Tag>
          {t('setting.yarnQueue.selectionHint')}
        </span>
      </p>
    </div>
  );
};

/* render memory option */
export const renderDynamicProperties = ({ model, field }: RenderCallbackParams) => {
  return (
    <div>
      <Input.TextArea
        rows={8}
        name="dynamicProperties"
        placeholder="Enter $key=$value,if there are multiple parameters,you can enter them on the new line(-D <arg>)"
        value={model[field]}
        onInput={(e: ChangeEvent) => (model[field] = e?.target?.value)}
      />
      <p class="conf-desc mt-10px">
        <span class="note-info">
          <Tag color="#2db7f5" class="tag-note">
            {t('flink.app.noteInfo.note')}
          </Tag>
          {t('flink.app.noteInfo.dynamicProperties')}
          <a
            href="https://ci.apache.org/projects/flink/flink-docs-stable/ops/config.html"
            target="_blank"
            class="pl-5px"
          >
            Flink {t('flink.app.noteInfo.officialDoc')}
          </a>
        </span>
      </p>
    </div>
  );
};

export const getAlertSvgIcon = (name: string, text: string) => {
  return (
    <Alert type="info">
      {{
        message: () => (
          <div>
            <SvgIcon class="mr-8px" name={name} style={{ color: '#108ee9' }} />
            <span>{text}</span>
          </div>
        ),
      }}
    </Alert>
  );
};
/* render application conf */
export const renderIsSetConfig = (
  model: Recordable,
  field: string,
  registerConfDrawer: Fn,
  openConfDrawer: Fn,
) => {
  /* Open the sqlConf drawer */
  async function handleSQLConf(checked: boolean) {
    if (checked) {
      if (unref(model.configOverride)) {
        openConfDrawer(true, {
          configOverride: unref(model.configOverride),
        });
      } else {
        const res = await handleConfTemplate();
        openConfDrawer(true, {
          configOverride: decodeByBase64(res),
        });
      }
    } else {
      openConfDrawer(false);
      Object.assign(model, {
        configOverride: null,
        isSetConfig: false,
      });
    }
  }
  function handleEditConfClose() {
    if (!model.configOverride) {
      model.isSetConfig = false;
    }
  }
  function handleMergeSubmit(data: { configOverride: string; isSetConfig: boolean }) {
    if (data.configOverride == null || !data.configOverride.replace(/^\s+|\s+$/gm, '')) {
      Object.assign(model, {
        configOverride: null,
        isSetConfig: false,
      });
    } else {
      Object.assign(model, {
        configOverride: data.configOverride,
        isSetConfig: true,
      });
    }
  }
  function handleConfChange(checked: boolean) {
    model[field] = checked;
    if (checked) {
      handleSQLConf(true);
    }
  }
  return (
    <div>
      <Switch
        checked-children="ON"
        un-checked-children="OFF"
        checked={model[field]}
        onChange={handleConfChange}
      />
      {model[field] && (
        <SettingTwoTone
          class="ml-10px"
          theme="twoTone"
          two-tone-color="#4a9ff5"
          onClick={() => handleSQLConf(true)}
        />
      )}

      <Mergely
        onOk={handleMergeSubmit}
        onClose={() => handleEditConfClose()}
        onRegister={registerConfDrawer}
      />
    </div>
  );
};

// render history version form item
export const renderSqlHistory = (
  { model, flinkSqlHistory },
  { handleChangeSQL, handleCompareOk }: { handleChangeSQL: Fn; handleCompareOk: Fn },
) => {
  const { createConfirm } = useMessage();
  const compareSQL = ref<string[]>([]);

  function handleSelectChange(value: SelectValue) {
    model.flinkSqlHistory = value;
    handleChangeSQL(value);
  }

  //version compact
  function handleCompactSQL() {
    createConfirm({
      title: () => (
        <div>
          <Icon icon="ant-design:swap-outlined" style="color: #4a9ff5" />
          <span class="pl-10px"> {t('flink.app.flinkSql.compare')} </span>
        </div>
      ),
      okText: t('flink.app.flinkSql.compare'),
      width: 600,
      content: () => {
        return (
          <Form class="!pt-30px">
            <Form.Item
              label={t('flink.app.flinkSql.version')}
              label-col={{ lg: { span: 5 }, sm: { span: 7 } }}
              wrapper-col={{ lg: { span: 16 }, sm: { span: 17 } }}
            >
              <Select
                placeholder={t('flink.app.flinkSql.compareFlinkSQL')}
                value={unref(compareSQL)}
                onChange={(value: any) => (compareSQL.value = value)}
                mode="multiple"
                maxTagCount={2}
              >
                {renderSelectOptions()}
              </Select>
            </Form.Item>
          </Form>
        );
      },
      onOk: () => handleCompareOk(compareSQL.value),
      onCancel: () => {
        compareSQL.value.length = 0;
      },
    });
  }

  const renderSelectOptions = (isCompareSelect = false) => {
    const isDisabled = (ver: Recordable) => {
      if (!isCompareSelect) return false;
      return compareSQL.value.length == 2 && compareSQL.value.findIndex((i) => i === ver.id) === -1;
    };
    console.log('flinkSqlHistory', flinkSqlHistory);
    return (flinkSqlHistory || []).map((ver) => {
      return (
        <Select.Option key={ver.id} disabled={isDisabled(ver)}>
          <div>
            <Button type="primary" shape="circle" size="small">
              {ver.version}
            </Button>
            {ver.effective && (
              <Tag color="green" style=";margin-left: 5px;" size="small">
                {t('flink.app.flinkSql.effectiveVersion')}
              </Tag>
            )}

            {[CandidateTypeEnum.NEW, CandidateTypeEnum.HISTORY].includes(ver.candidate) && (
              <Tag color="cyan" style=";margin-left: 5px;" size="small">
                {t('flink.app.flinkSql.candidateVersion')}
              </Tag>
            )}

            <span style="color: darkgrey">
              <Icon icon="ant-design:clock-circle-outlined" />
              {ver.createTime}
            </span>
          </div>
        </Select.Option>
      );
    });
  };

  return (
    <div>
      <Select
        onChange={(value: SelectValue) => handleSelectChange(value)}
        value={model.flinkSqlHistory}
        style="width: calc(100% - 60px)"
      >
        {renderSelectOptions()}
      </Select>
      <Button type="primary" class="ml-10px w-50px" onClick={() => handleCompactSQL()}>
        <Icon icon="ant-design:swap-outlined" />
      </Button>
    </div>
  );
};

export const renderCompareSelectTag = (ver: any) => {
  return (
    <div>
      <Button type="primary" shape="circle" size="small">
        {ver.version}
      </Button>
      {ver.effective && (
        <Tag color="green" style=";margin-left: 5px;" size="small">
          {t('flink.app.flinkSql.effectiveVersion')}
        </Tag>
      )}

      {[CandidateTypeEnum.NEW, CandidateTypeEnum.HISTORY].includes(ver.candidate) && (
        <Tag color="cyan" style=";margin-left: 5px;" size="small">
          {t('flink.app.flinkSql.candidateVersion')}
        </Tag>
      )}
    </div>
  );
};

export const renderResourceFrom = (model: Recordable) => {
  return (
    <Select
      onChange={(value: string) => (model.resourceFrom = value)}
      value={model.resourceFrom}
      placeholder="Please select resource from"
    >
      <Select.Option value="1">
        <SvgIcon name="github" />
        <span class="pl-10px">Project</span>
        <span class="gray"> (build from Project)</span>
      </Select.Option>
      <Select.Option value="2">
        <SvgIcon name="upload" />
        <span class="pl-10px">Upload</span>
        <span class="gray"> (upload local job)</span>
      </Select.Option>
    </Select>
  );
};

export const renderStreamParkResource = ({ model, resources }) => {
  const renderOptions = () => {
    return (resources || [])
      .filter((item) => item.resourceType !== ResourceTypeEnum.FLINK_APP)
      .map((resource) => {
        return (
          <Select.Option
            key={resource.id}
            label={resource.resourceType + '-' + resource.resourceName}
          >
            <div>
              <Tag color="green" style=";margin-left: 5px;" size="small">
                {resource.resourceType}
              </Tag>
              <span style="color: darkgrey">{resource.resourceName}</span>
            </div>
          </Select.Option>
        );
      });
  };

  return (
    <div>
      <Select
        show-search
        allow-clear
        optionFilterProp="label"
        mode="multiple"
        max-tag-count={3}
        onChange={(value) => (model.teamResource = value)}
        value={model.teamResource}
        placeholder={t('flink.app.resourcePlaceHolder')}
      >
        {renderOptions()}
      </Select>
    </div>
  );
};

export const renderStreamParkJarApp = ({ model, resources }) => {
  function handleAppChange(value: SelectValue) {
    const res = resources.filter((item) => item.id == value)[0];
    model.mainClass = res.mainClass;
    model.uploadJobJar = res.resourceName;
  }

  const renderOptions = () => {
    console.log('resources', resources);
    return (resources || [])
      .filter((item) => item.resourceType == ResourceTypeEnum.FLINK_APP)
      .map((resource) => {
        return (
          <Select.Option key={resource.id} label={resource.resourceName}>
            <div>
              <Tag color="green" style=";margin-left: 5px;" size="small">
                {resource.resourceType}
              </Tag>
              <span style="color: darkgrey">{resource.resourceName}</span>
            </div>
          </Select.Option>
        );
      });
  };

  return (
    <div>
      <Select
        show-search
        allow-clear
        optionFilterProp="label"
        onChange={handleAppChange}
        value={model.uploadJobJar}
        placeholder={t('flink.app.selectAppPlaceHolder')}
      >
        {renderOptions()}
      </Select>
    </div>
  );
};

export const renderFlinkAppRestoreMode = ({ model, field }: RenderCallbackParams) => {
  return (
    <div>
      <Select
        value={model[field]}
        onChange={(value) => (model[field] = value)}
        placeholder="Please select restore mode"
      >
        <Select.Option key="no_claim" value={RestoreModeEnum.NO_CLAIM}>
          <Tag color="gray" style=";margin-left: 5px;" size="small">
            NO_CLAIM
          </Tag>
        </Select.Option>
        <Select.Option key="claim" value={RestoreModeEnum.CLAIM}>
          <Tag color="#108ee9" style=";margin-left: 5px;" size="small">
            CLAIM
          </Tag>
        </Select.Option>
        <Select.Option key="legacy" value={RestoreModeEnum.LEGACY}>
          <Tag color="#8E50FF" style=";margin-left: 5px;" size="small">
            LEGACY
          </Tag>
        </Select.Option>
      </Select>
      <p class="mt-10px">
        <span class="note-info">
          <Tag color="#2db7f5" class="tag-note" size="small">
            {t('flink.app.noteInfo.note')}
          </Tag>
          <span class="tip-info">{t('flink.app.restoreModeTip')}</span>
        </span>
      </p>
    </div>
  );
};
