<script setup lang="ts">
import { reactive, ref, onMounted, toRaw } from 'vue';
import { PageContainer } from 'antdv-pro-layout';
import { Modal, message } from 'ant-design-vue/es';
import CodemirrorEdite from '@/components/CodemirrorEdite/index.vue';
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
import useNeInfoStore from '@/store/modules/neinfo';
import { regExpIPv4, regExpIPv6 } from '@/utils/regular-utils';
import useI18n from '@/hooks/useI18n';
import { getMMLByUDM, sendMMlByUDM } from '@/api/mmlManage/udmOperate';
import { UploadRequestOption } from 'ant-design-vue/es/vc-upload/interface';
import { uploadFileToNE } from '@/api/tool/file';
const { t } = useI18n();

/**网元参数 */
let neOptions = ref<Record<string, any>[]>([]);

/**对象信息状态类型 */
type StateType = {
  /**网元ID */
  neId: string | undefined;
  /**命令数据 tree */
  mmlTreeData: any[];
  /**命令选中 */
  mmlSelect: Record<string, any>;
  /**表单数据 */
  from: Record<string, any>;
  /**自动完成 input */
  autoCompleteValue: string;
  /**自动完成 options */
  autoCompleteData: any[];
  /**自动完成 options */
  autoCompleteSearch: any[];
  /**命令发送日志 */
  mmlCmdLog: string;
};

/**对象信息状态 */
let state: StateType = reactive({
  neId: undefined,
  mmlTreeData: [],
  mmlSelect: {
    title: '',
    key: '',
    operation: '',
    object: '',
    param: [],
  },
  from: {
    uploadLoading: false,
    sendLoading: false,
  },
  autoCompleteValue: '',
  autoCompleteData: [],
  autoCompleteSearch: [],
  mmlCmdLog: '',
});

/**查询可选命令列表 */
function fnTreeSelect(_: any, info: any) {
  state.mmlSelect = info.node.dataRef;
  state.from = {};
  // 遍历判断是否有初始value
  if (Array.isArray(state.mmlSelect.param)) {
    for (const param of state.mmlSelect.param) {
      if (typeof param.value !== 'undefined' && param.value != '') {
        const valueType = param.type;
        if (['enum', 'int'].includes(valueType)) {
          state.from[param.name] = Number(param.value);
        } else if (valueType === 'bool') {
          state.from[param.name] = Boolean(param.value);
        } else {
          state.from[param.name] = param.value;
        }
      }
    }
  }

  state.autoCompleteValue =
    `${state.mmlSelect.operation} ${state.mmlSelect.object}`.trim();
  // state.mmlCmdLog = '';
  // 回到顶部
  window.scrollTo({
    top: 0,
    behavior: 'smooth', // 平滑滚动到顶部，如果不需要平滑效果可以将此行代码删除
  });
}

/**清空控制台日志 */
function fnCleanCmdLog() {
  state.mmlCmdLog = '';
}

/**清空表单 */
function fnCleanFrom() {
  state.mmlSelect = {
    title: '',
    key: '',
    operation: '',
    object: '',
    param: [],
  };
  state.from = {};
}

/**命令发送 */
function fnSendMML() {
  if (state.from.sendLoading) {
    return;
  }
  if (!state.neId) {
    message.warning({
      content: t('views.mmlManage.udmOpesrate.noUDM'),
      duration: 5,
    });
    return;
  }

  let cmdArr: string[] = [];
  const operation = state.mmlSelect.operation;
  const object = state.mmlSelect.object;
  // 根据参数取值
  let argsArr: string[] = [];
  const param = toRaw(state.mmlSelect.param) || [];
  if (operation && Array.isArray(param)) {
    const from = toRaw(state.from);
    for (const item of param) {
      const value = from[item.name];

      // 是否必填项且有效值
      const notV = value === null || value === undefined || value === '';
      if (item.optional === 'false' && notV) {
        message.warning(t('views.mmlManage.require', { num: item.display }), 2);
        return;
      }

      // 检查是否存在值
      if (!Reflect.has(from, item.name) || notV) {
        continue;
      }

      // 检查规则
      const [ok, msg] = ruleVerification(item, from[item.name]);
      if (!ok) {
        message.warning({
          content: `${msg}`,
          duration: 3,
        });
        return;
      }

      argsArr.push(`${item.name}=${from[item.name]}`);
    }

    // 拼装命令
    const argsStr = argsArr.join(',');
    let cmdStr = '';
    if (object && argsStr) {
      cmdStr = `${operation} ${object}:${argsStr}`;
    } else if (object) {
      cmdStr = `${operation} ${object}`;
    } else {
      cmdStr = `${operation} ${argsStr}`;
    }
    cmdArr = [cmdStr.trim()];
  }

  if (cmdArr.length > 0) {
    state.autoCompleteValue = cmdArr[0];
  } else {
    let value = state.autoCompleteValue;
    if (value.indexOf('\n') !== -1) {
      value = value.replace(/(\r\n|\n)/g, ';');
    }
    cmdArr = value.split(';');
  }

  // 发送
  state.from.sendLoading = true;
  sendMMlByUDM(state.neId, cmdArr)
    .then(res => {
      state.from.sendLoading = false;
      if (res.code === RESULT_CODE_SUCCESS) {
        let resultArr = res.data;
        for (let i = 0; i < resultArr.length; i++) {
          const str = resultArr[i] || '';
          const logStr = str.replace(/(\r\n|\n)/g, '\n');
          const cmdStr = cmdArr[i] || '';
          state.mmlCmdLog += `${cmdStr}\n${logStr}\n`;
        }
      } else {
        state.mmlCmdLog += `${res.msg}\n`;
      }
    })
    .finally(() => {
      // 控制台滚动底部
      const container = document.getElementsByClassName('cm-scroller')[0];
      if (container) {
        container.scrollTop = container.scrollHeight;
      }
    });
}

/**上传变更 */
function fnUpload(up: UploadRequestOption, name: string) {
  const neId = state.neId;
  if (!neId) {
    message.warning({
      content: t('views.mmlManage.udmOpesrate.noUDM'),
      duration: 5,
    });
    return;
  }
  Modal.confirm({
    title: t('common.tipTitle'),
    content: t('views.mmlManage.uploadFileTip'),
    onOk() {
      const hide = message.loading(t('common.loading'), 0);
      state.from.uploadLoading = true;
      uploadFileToNE('UDM', neId, up.file as File, 5)
        .then(res => {
          // 文件转存
          if (res.code === RESULT_CODE_SUCCESS) {
            message.success(t('views.mmlManage.uploadFileOk'), 3);
            state.from[name] = res.data;
          } else {
            message.error(t('views.mmlManage.uploadFileErr'), 3);
          }
        })
        .finally(() => {
          state.from.uploadLoading = false;
          hide();
        });
    },
  });
}

/**规则校验 */
function ruleVerification(
  row: Record<string, any>,
  value: any
): (string | boolean)[] {
  let result = [true, ''];
  const type = row.type;
  const filter = row.filter;
  const display = row.display;

  switch (type) {
    case 'int':
      if (filter && filter.indexOf('~') !== -1) {
        const filterArr = filter.split('~');
        const minInt = parseInt(filterArr[0]);
        const maxInt = parseInt(filterArr[1]);
        const valueInt = parseInt(value);
        if (valueInt < minInt || valueInt > maxInt) {
          return [false, t('views.mmlManage.requireInt', { display, filter })];
        }
      }
      break;
    case 'ipv4':
      if (!regExpIPv4.test(value)) {
        return [false, t('views.mmlManage.requireIpv4', { display })];
      }
      break;
    case 'ipv6':
      if (!regExpIPv6.test(value)) {
        return [false, t('views.mmlManage.requireIpv6', { display })];
      }
      break;
    case 'enum':
      if (filter && filter.indexOf('{') === 1) {
        let filterJson: Record<string, any> = {};
        try {
          filterJson = JSON.parse(filter); //string---json
        } catch (error) {
          console.error(error);
        }

        if (!Object.keys(filterJson).includes(`${value}`)) {
          return [false, t('views.mmlManage.requireEnum', { display })];
        }
      }
      break;
    case 'bool':
      if (filter && filter.indexOf('{') === 1) {
        let filterJson: Record<string, any> = {};
        try {
          filterJson = JSON.parse(filter); //string---json
        } catch (error) {
          console.error(error);
        }

        if (!Object.values(filterJson).includes(`${value}`)) {
          return [false, t('views.mmlManage.requireBool', { display })];
        }
      }
      break;
    case 'string':
      if (filter && filter.indexOf('~') !== -1) {
        try {
          const filterArr = filter.split('~');
          let rule = new RegExp(
            '^\\S{' + filterArr[0] + ',' + filterArr[1] + '}$'
          );
          if (!rule.test(value)) {
            return [false, t('views.mmlManage.requireString', { display })];
          }
        } catch (error) {
          console.error(error);
        }
      }

      break;
    case 'regex':
      if (filter) {
        try {
          let regex = new RegExp(filter);
          if (!regex.test(value)) {
            return [false, t('views.mmlManage.requireString', { display })];
          }
        } catch (error) {
          console.error(error);
        }
      }
      break;
    case 'file':
      if (filter) {
        const arr: string[] = filter.split(',');
        const itemArr = arr.filter(item => value.endsWith(item));
        if (itemArr.length === 0) {
          return [false, t('views.mmlManage.requireFile', { display })];
        }
      }
      break;

    default:
      console.warn(t('views.mmlManage.requireUn', { display }), type);
  }
  return result;
}

/**查询可选命令列表 */
function fnGetList() {
  getMMLByUDM().then(res => {
    if (res.code === RESULT_CODE_SUCCESS) {
      // 构建自动完成筛选结构
      const autoCompleteArr: Record<string, any>[] = [];
      // 构建树结构
      const treeArr: Record<string, any>[] = [];
      for (const item of res.data) {
        const id = item['id'];
        const object = item['object'];
        const operation = item['operation'];
        const mmlDisplay = item['mmlDisplay'];
        // 可选属性参数
        let param = [];
        try {
          param = JSON.parse(item['paramJson']);
        } catch (error) {
          console.error(error);
        }

        // 遍历检查大类
        const treeItemIndex = treeArr.findIndex(i => i.key == item['category']);
        if (treeItemIndex < 0) {
          treeArr.push({
            title: item['catDisplay'],
            key: item['category'],
            selectable: false,
            children: [
              { key: id, title: mmlDisplay, object, operation, param },
            ],
          });
          autoCompleteArr.push({
            value: item['catDisplay'],
            key: item['category'],
            selectable: false,
            options: [{ key: id, value: mmlDisplay, object, operation, param }],
          });
        } else {
          treeArr[treeItemIndex].children.push({
            key: id,
            title: mmlDisplay,
            object,
            operation,
            param,
          });
          autoCompleteArr[treeItemIndex].options.push({
            key: id,
            value: mmlDisplay,
            object,
            operation,
            param,
          });
        }
      }
      state.mmlTreeData = treeArr;
      state.autoCompleteData = autoCompleteArr;
    } else {
      message.warning({
        content: t('views.mmlManage.cmdNoTip', { num: 'UDM' }),
        duration: 2,
      });
    }
  });
}

/**自动完成搜索匹配前缀 */
function fnAutoCompleteSearch(value: string) {
  state.autoCompleteSearch = [];
  for (const item of state.autoCompleteData) {
    const filterOptions = item.options.filter((s: any) => {
      return `${s.operation} ${s.object}`.indexOf(value.toLowerCase()) >= 0;
    });
    if (filterOptions.length > 0) {
      state.autoCompleteSearch.push({
        value: item.value,
        key: item.value,
        selectable: false,
        options: filterOptions,
      });
    }
  }
}

/**自动完成搜索选择 */
function fnAutoCompleteSelect(_: any, option: any) {
  state.mmlSelect = {
    title: option.value,
    key: option.key,
    operation: option.operation,
    object: option.object,
    param: option.param,
  };
  state.from = {};
  state.autoCompleteValue = `${option.operation} ${option.object}`.trim();
}

/**自动完成搜索选择 */
function fnAutoCompleteChange(value: any, _: any) {
  if (value.indexOf(';') !== -1 || value.indexOf('\n') !== -1) {
    fnCleanFrom();
    return;
  }
  for (const item of state.autoCompleteData) {
    const findItem = item.options.find((s: any) => {
      const prefix = `${s.operation} ${s.object}`;
      return value.startsWith(prefix);
    });
    if (findItem) {
      state.mmlSelect = {
        title: findItem.value,
        key: findItem.key,
        operation: findItem.operation,
        object: findItem.object,
        param: findItem.param,
      };
      state.from = {};
      // 截取拆分赋值
      const prefix = `${findItem.operation} ${findItem.object}:`;
      const argsStr = value.replace(prefix, '');
      if (argsStr.length > 3) {
        const argsArr = argsStr.split(',');
        for (const arg of argsArr) {
          const kvArr = arg.split('=');
          if (kvArr.length >= 2) {
            state.from[kvArr[0]] = kvArr[1];
          }
        }
      }
      break;
    } else {
      state.mmlSelect = {
        title: '',
        key: '',
        operation: '',
        object: '',
        param: [],
      };
    }
  }
}

/**自动完成按键触发 */
function fnAutoCompleteKeydown(evt: any) {
  if (evt.key === 'Enter') {
    // 阻止默认的换行行为
    evt.preventDefault();
    // 按下 Shift + Enter 键时换行
    if (evt.shiftKey) {
      // 插入换行符
      const textarea = evt.target;
      const start = textarea.selectionStart;
      const end = textarea.selectionEnd;
      const text = textarea.value;
      textarea.value = text.substring(0, start) + '\n' + text.substring(end);
      state.autoCompleteValue = textarea.value;
      // 更新光标位置
      textarea.selectionStart = textarea.selectionEnd = start + 1;
    } else {
      fnSendMML();
    }
  }
}

onMounted(() => {
  // 获取网元网元列表
  useNeInfoStore()
    .fnNelist()
    .then(res => {
      if (res.code === RESULT_CODE_SUCCESS) {
        if (res.data.length > 0) {
          // 获取可用UDM
          const udmArr = useNeInfoStore().getNeSelectOtions.find(
            item => item.value === 'UDM'
          );
          if (udmArr) {
            neOptions.value = udmArr.children;
          }
          // 是否有可选项
          if (neOptions.value.length > 0) {
            state.neId = neOptions.value[0].value;
            // 获取列表数据
            fnGetList();
          } else {
            message.warning({
              content: t('views.mmlManage.udmOpesrate.noUDM'),
              duration: 5,
            });
          }
        } else {
          message.warning({
            content: t('views.mmlManage.udmOpesrate.noUDM'),
            duration: 5,
          });
        }
      } else {
        message.warning({
          content: t('common.noData'),
          duration: 2,
        });
      }
    });
});
</script>

<template>
  <PageContainer>
    <a-row :gutter="16">
      <a-col :lg="6" :md="6" :xs="24" style="margin-bottom: 24px">
        <!-- 命令导航 -->
        <a-card
          size="small"
          :bordered="false"
          :title="t('views.mmlManage.cmdTitle')"
        >
          <a-form layout="vertical" autocomplete="off">
            <a-form-item name="neId ">
              <a-select
                v-model:value="state.neId"
                :options="neOptions"
                :placeholder="t('common.selectPlease')"
              />
            </a-form-item>
            <a-form-item name="mmlTree" v-if="state.mmlTreeData.length > 0">
              <a-directory-tree
                :tree-data="state.mmlTreeData"
                default-expand-all
                @select="fnTreeSelect"
                :selectedKeys="[state.mmlSelect.key]"
              ></a-directory-tree>
            </a-form-item>
          </a-form>
        </a-card>
      </a-col>
      <a-col :lg="18" :md="18" :xs="24">
        <!-- 命令参数输入 -->
        <a-card size="small" :bordered="false">
          <template #title>
            <a-typography-text strong v-if="state.mmlSelect.title">
              {{ state.mmlSelect.title }}
            </a-typography-text>
            <a-typography-text type="danger" v-else>
              {{ t('views.mmlManage.cmdOpTip') }}
            </a-typography-text>
          </template>
          <!-- 插槽-卡片右侧 -->
          <template #extra>
            <a-space :size="8">
              <a-button
                type="default"
                size="small"
                @click.prevent="fnCleanFrom"
                v-if="!!state.mmlSelect.param"
              >
                <template #icon>
                  <ClearOutlined />
                </template>
                {{ t('views.mmlManage.clearForm') }}
              </a-button>
              <a-button
                type="primary"
                size="small"
                :loading="state.from.sendLoading"
                @click.prevent="fnSendMML"
              >
                <template #icon>
                  <SendOutlined />
                </template>
                {{ t('views.mmlManage.exec') }}
              </a-button>
            </a-space>
          </template>

          <a-form
            layout="horizontal"
            autocomplete="off"
            :validate-on-rule-change="false"
            :validateTrigger="[]"
          >
            <a-form-item
              :label="t('views.mmlManage.cmdQuickEntry')"
              :help="t('views.mmlManage.cmdQuickEntryHelp')"
            >
              <a-auto-complete
                v-model:value="state.autoCompleteValue"
                :dropdown-match-select-width="500"
                style="width: 100%"
                :options="state.autoCompleteSearch"
                @search="fnAutoCompleteSearch"
                @select="fnAutoCompleteSelect"
                @change="fnAutoCompleteChange"
                @keydown="fnAutoCompleteKeydown"
              >
                <a-textarea :placeholder="t('common.inputPlease')" auto-size />
              </a-auto-complete>
            </a-form-item>
          </a-form>

          <template v-if="state.mmlSelect.operation && state.mmlSelect.param">
            <a-form
              layout="vertical"
              autocomplete="off"
              :validate-on-rule-change="false"
              :validateTrigger="[]"
            >
              <a-divider orientation="left">
                {{ t('views.mmlManage.cmdParamPanel') }}
              </a-divider>
              <a-row :gutter="16">
                <a-col
                  :lg="6"
                  :md="12"
                  :xs="24"
                  v-for="item in state.mmlSelect.param"
                >
                  <a-form-item
                    :label="item.display"
                    :name="item.name"
                    :required="item.optional === 'false'"
                  >
                    <a-tooltip>
                      <template #title v-if="item.comment">
                        {{ item.comment }}
                      </template>

                      <a-input-number
                        v-if="item.type === 'int'"
                        v-model:value="state.from[item.name]"
                        style="width: 100%"
                      ></a-input-number>
                      <a-switch
                        v-else-if="item.type === 'bool'"
                        v-model:checked="state.from[item.name]"
                        :checked-children="t('common.switch.open')"
                        :un-checked-children="t('common.switch.shut')"
                      ></a-switch>
                      <a-select
                        v-else-if="item.type === 'enum'"
                        v-model:value="state.from[item.name]"
                        :allow-clear="item.optional === 'true'"
                      >
                        <a-select-option
                          :value="v"
                          :key="v"
                          v-for="(k, v) in JSON.parse(item.filter)"
                        >
                          {{ k }}
                        </a-select-option>
                      </a-select>
                      <a-input-group compact v-else-if="item.type === 'file'">
                        <a-input
                          v-model:value="state.from[item.name]"
                          style="width: calc(100% - 32px)"
                        />
                        <a-upload
                          name="file"
                          list-type="text"
                          :accept="item.filter"
                          :max-count="1"
                          :show-upload-list="false"
                          :custom-request="(v:any) => fnUpload(v, item.name)"
                        >
                          <a-button
                            type="primary"
                            :loading="state.from.uploadLoading"
                          >
                            <template #icon>
                              <UploadOutlined />
                            </template>
                          </a-button>
                        </a-upload>
                      </a-input-group>
                      <a-input
                        v-else
                        v-model:value="state.from[item.name]"
                      ></a-input>
                    </a-tooltip>
                  </a-form-item>
                </a-col>
              </a-row>
            </a-form>
          </template>
        </a-card>

        <!-- 命令展示 -->
        <a-card
          :title="t('views.mmlManage.cmdConsole')"
          :bordered="false"
          size="small"
          :body-style="{ padding: 0 }"
          style="margin-top: 16px"
          v-show="state.mmlCmdLog"
        >
          <!-- 插槽-卡片右侧 -->
          <template #extra>
            <a-space :size="8" align="center">
              <a-button
                type="default"
                size="small"
                @click.prevent="fnCleanCmdLog"
              >
                <template #icon>
                  <ClearOutlined />
                </template>
                {{ t('views.mmlManage.clearLog') }}
              </a-button>
            </a-space>
          </template>

          <CodemirrorEdite
            :value="state.mmlCmdLog"
            :disabled="true"
            height="500px"
          ></CodemirrorEdite>
        </a-card>
      </a-col>
    </a-row>
  </PageContainer>
</template>

<style lang="less" scoped></style>
