<script lang="ts">
import { defineComponent } from "vue";
export default defineComponent({
  name: "ModelReasoningResult",
});
</script>

<script setup lang="ts">
import {
  Button,
  Form,
  FormItem,
  Select,
  SelectOption,
  Progress,
  message,
  Row,
  Col,
  Textarea,
  Spin,
  Tooltip,
} from "ant-design-vue";
import screenfull from "screenfull";
import ModuleCard from "@/views/dataManage/index/components/Card/ModuleCard.vue";
import Icon from "@/components/Module/Icon/index.vue";
import { ref, unref, computed, onUnmounted, nextTick } from "vue";

interface PropsType {
  state: Record<string, any>;
  api: Record<string, any>;
  requestMap: Record<string, any>;
  // data: Record<string, any>[];
}

const props = withDefaults(defineProps<PropsType>(), {
  state: () => {
    return {};
  },
  api: () => {
    return {};
  },
  requestMap: () => {
    return {};
  },
});

// 选项获取loading
const optionsLoading = computed(() => {
  return (
    props.requestMap.testDatasetListRequest.loading.value ||
    props.requestMap.modelListRequest.loading.value ||
    props.requestMap.reasoningOptionsRequest.loading.value
  );
});

// 系统信息loading
const systemInfoLoading = computed(() => {
  return (
    props.requestMap.cpuUsageRequest.loading.value ||
    props.requestMap.gpuUsageRequest.loading.value ||
    props.requestMap.memoryInfoRequest.loading.value ||
    props.requestMap.gpuInfoRequest.loading.value
  );
});

const formRef = ref();

const form = ref<Record<string, any>>({
  modelId: null,
  type: null,
  source: null,
  checkpointDir: null,
  scriptCommand: null,
  description: null,
});

const selectDropStyle = {
  width: "fit-content",
  minWidth: "fit-content",
  maxWidth: "fit-content",
};

// 模型选项
const modelsOptions = computed(() => {
  return props.state.models.value.map((item: Record<string, any>) => {
    return {
      label: item.name,
      value: item.id,
    };
  });
});

// 推理类型选项
const typeOptiosn = computed(() => {
  return props.state.resoningTypes.value.map((item: string) => {
    return {
      label: item,
      value: item,
    };
  });
});

// 数据来源
const sourceOptions = computed(() => {
  return props.state.testDatasetList.value.map((item: Record<string, any>) => {
    return {
      label: item.name,
      value: item.id,
    };
  });
});

// 获取模型权重目录
const modelDirOptions = computed(() => {
  if (!form.value.modelId) return [];
  else {
    const { directories } = props.state.modelWeightOptions.value;
    if (!directories?.length) return [];
    const dirs = directories.map((item: Record<string, any>) => {
      return {
        label: item?.description,
        value: item?.id,
      };
    });
    console.log("props.state.modelWeightOptions", dirs);
    return dirs;
  }
});

// 模型切换
const onModelChange = (e: string | any, ...args: any) => {
  if (e) {
    console.log("e", e);
    const model = props.state.models.value.find((item: Record<string, any>) => {
      return item.id === e;
    });
    if (model) {
      onGetModelWeightDir(e);
      scriptOptions.value = model.config_files.split(",");
      form.value.scriptCommand = "";
    }
  }
};

// 获取模型权重
const onGetModelWeightDir = async (modelId: string) => {
  await props.api.getModelWeightOptions(modelId);
};

const cardList = [
  {
    label: "CPU使用率",
    key: "cpuUsage",
  },
  {
    label: "GPU使用率",
    key: "gpuUsage",
  },
  {
    label: "内存使用",
    key: "physicalMemory",
  },
  {
    label: "显存使用",
    key: "gpuInfo",
  },
];

// 回显
const sourceFieldFormat = (field: string) => {
  if (field === "cpuUsage") {
    const { cpu_usage_percent, unit } = props.state.cpuUsage.value;
    if (unit == "percent") {
      return `${cpu_usage_percent || "0.0"}%`;
    } else {
      return cpu_usage_percent || "0.0%";
    }
  } else if (field === "gpuUsage") {
    const { gpu_usage_percent } = props.state.gpuUsage.value;
    return `${gpu_usage_percent || 0}%`;
  } else if (field === "physicalMemory") {
    const { physical_memory } = props.state.memoryUsage.value;
    return physical_memory?.used || "0.0 GB";
  } else if (field === "gpuInfo") {
    const raw = props?.state?.gpuInfo?.value?.memory_used || 0;
    return `${(raw / 1024).toFixed(1)} GB`;
  } else {
    return "";
  }
};

const taskLoading = computed(() => {
  return props.requestMap.createTaskRequest.loading.value || timeCount;
});

// 校验表单
const validForm = () => {
  let valid = true;
  let tips = "";
  const raw = unref(form);
  if (valid && !raw.modelId) {
    tips = "请选择模型";
    valid = false;
  } else if (valid && !raw.type) {
    tips = "请选择推理类型";
    valid = false;
  } else if (valid && !raw.source) {
    tips = "请选择数据来源";
    valid = false;
  } else if (valid && !raw.checkpointDir) {
    tips = "请选择权重";
    valid = false;
  } else if (valid && !raw.description) {
    tips = "请输入任务描述";
    valid = false;
  }
  // else if (valid && !raw.scriptCommand) {
  //   tips = "请输入运行命令";
  //   valid = false;
  // }
  if (!valid) {
    message.error(tips);
  }
  return valid;
};

const statusMap: Record<string, any> = {
  running: "进行中",
  success: "已完成",
  error: "失败",
};

const statusColorMap: Record<string, any> = {
  running: {
    light: "#BFDBFE",
    dark: "#0057FE",
  },
  success: {
    light: "#BFDBFE",
    dark: "#0057FE",
  },
  error: {
    light: "#FFDEDE",
    dark: "#FF2626",
  },
};

const showStatus = computed(() => {
  if (props.state.relationTaskId.value) {
    {
      {
        props.state.taskStatus.value?.status;
      }
    }
    return statusMap[props.state.taskStatus.value?.status] || "进行中";
  } else {
    return "未开始";
  }
});

const showProgress = computed(() => {
  if (props.state.relationTaskId.value) {
    return props.state.taskStatus.value?.progress || 0;
  } else {
    return 0;
  }
});

let logTimeout: undefined | NodeJS.Timeout = undefined;
// 日志reader
let logRead: null | ReadableStreamDefaultReader = null;

const logContainerRef = ref();

const onScrollBottom = (dom: HTMLElement) => {
  if (dom) {
    dom.scrollTop = dom.scrollHeight;
  }
};

// 销毁日志
const logCleanup = async () => {
  props.api.log.onLogInterrupt();
  await onLogReadDestory();
};

// 销毁日志reader
const onLogReadDestory = async () => {
  if (logRead) {
    try {
      await logRead.cancel();
      await logRead.releaseLock();
    } catch (error: unknown) {
      console.warn("释放锁时出错:", error);
    } finally {
      logRead = null;
    }
  }
};

// 销毁日志读取定时器
const clearLogTimecount = () => {
  if (logTimeout) {
    clearTimeout(logTimeout);
    logTimeout = undefined;
  }
};

// 启动定时器
const startLogTimecount = () => {
  if (!logRead) {
    clearLogTimecount();
    return;
  }
  clearLogTimecount();
  const decoder = new TextDecoder("utf-8");
  logTimeout = setInterval(async () => {
    // @ts-ignore
    const { done, value } = await logRead?.read();
    if (done) {
      // 清除定时器
      clearLogTimecount();
      await logCleanup();
    } else {
      const chunk = decoder.decode(value, { stream: false });
      const chunks = chunk.split("\n");
      props.state.log.logs.value.push(...chunks);
      nextTick(() => {
        onScrollBottom(logContainerRef.value);
      });
    }
  }, 200);
};

// 开始推理
const onStart = async () => {
  const valid = validForm();
  if (!valid) return;
  const raw = unref(form);
  const params = {
    // config_file_path: raw.scriptCommand,
    train_task_id: raw.checkpointDir,
    dataset_id: raw.source,
    model_id: raw.modelId,
    infer_type: raw.type,
    task_description: raw.description,
  };
  props.api.cleanTaskId();
  const res = await props.api.createTask(params);
  console.log("taskRes", res);
  if (res?.code == 200) {
    props.api.resetTaskStatus();
    // props.state.taskStatus.value = {};
    const taskId = props.state.relationTaskId.value;
    props.api?.getTaskList();
    await logCleanup();
    const logRes = await props.api.log.getLogs(taskId);
    logRead = logRes.body?.getReader();
    onStartStatusTimeCount();
    startLogTimecount();
  }
};

let scriptOptions = ref([]);

// 停止推理
const onStop = async () => {
  const taskId = props.state.relationTaskId.value;
  // 停止任务
  const res = await props.api.onStopTask(taskId);
  if (res?.code == 200) {
    // 中断状态更新
    props.api.onTaskStatusInterrupt();
    // 断开日志流
    await logCleanup();
    props.api.log.clearLogs();
    // 清除关联task
    props.api.cleanTaskId();
    // 销毁定时器
    clearLogTimecount();
    onCleanStatusTimeCount();
    // 重置状态
    props.api.resetTaskStatus();
    props.api?.getTaskList();
  }
};

let timeCount: any = undefined;

// 清除定时器
const onCleanStatusTimeCount = () => {
  if (timeCount) {
    clearInterval(timeCount);
    timeCount = undefined;
  }
};

// 状态更新定时器
const onStartStatusTimeCount = () => {
  onCleanStatusTimeCount();
  timeCount = setInterval(async () => {
    if (!props.state.relationTaskId.value) return;
    const res = await props.api.getTaskStatus(props.state.relationTaskId.value);
    if (res?.code == 200) {
      const { status, progress } = res.data;
      if (["error", "success"].includes(status)) {
        onCleanStatusTimeCount();
        props.api?.getTaskList();
        // props.api.cleanTaskId();
      }
    }
  }, 5 * 1000);
};

const createtaskVisible = computed(() => {
  // 没有任务，或者任务已经完成
  if (
    !props.state.relationTaskId.value ||
    ["error", "success"].includes(props.state.taskStatus.value?.status)
  ) {
    return true;
  } else {
    return false;
  }
});

const interruptVisible = computed(() => {
  // 有任务，同时任务状态非完成
  if (
    props.state.relationTaskId.value &&
    !["error", "success"].includes(props.state.taskStatus.value?.status)
  ) {
    return true;
  } else {
    return false;
  }
});

const logDomRef = ref();
const isFull = ref(false);

// 全屏切换
const onLogZoomChange = () => {
  if (screenfull.isEnabled) {
    if (logDomRef.value) {
      isFull.value = !screenfull.isFullscreen;
      screenfull.toggle(logDomRef.value); // 切换全屏状态
    }
  } else {
    console.log("您的浏览器不支持全屏功能");
  }
};

// 代码锁定
const codeDisabled = ref(true);

const onCodeDsiabledToggle = () => {
  codeDisabled.value = !codeDisabled.value;
};

const onModelDirChange = (value: string | null) => {
  form.value.checkpointDir = value;
};

onUnmounted(() => {
  onCleanStatusTimeCount();
  clearLogTimecount();
});

const prefix = "model-reasoning";
</script>

<template>
  <div :class="prefix">
    <div :class="`${prefix}-wrapper`">
      <div
        :class="{
          [`${prefix}-grid`]: true,
          [`${prefix}-grid--form`]: true,
        }"
      >
        <ModuleCard title="模型推理">
          <Spin :spinning="optionsLoading">
            <Form :model="form" ref="formRef" layout="vertical" :class="`${prefix}-form`">
              <Row :gutter="[10, 0]">
                <Col :span="12">
                  <FormItem label="选择推理模型">
                    <Select
                      placeholder="请选择推理模型"
                      v-model:value="form.modelId"
                      @change="onModelChange"
                      :dropdownStyle="selectDropStyle"
                    >
                      <SelectOption v-for="item in modelsOptions" :value="item.value">{{
                        item.label
                      }}</SelectOption>
                    </Select>
                  </FormItem>
                </Col>
                <Col :span="12">
                  <FormItem label="推理模式">
                    <Select
                      placeholder="请选择推理模式"
                      v-model:value="form.type"
                      :dropdownStyle="selectDropStyle"
                    >
                      <SelectOption v-for="item in typeOptiosn" :value="item.value">{{
                        item.label
                      }}</SelectOption>
                    </Select>
                  </FormItem>
                </Col>
              </Row>
              <Row :gutter="[10, 0]">
                <Col :span="12">
                  <FormItem label="数据来源">
                    <Select
                      placeholder="请选择数据来源"
                      v-model:value="form.source"
                      :dropdownStyle="selectDropStyle"
                    >
                      <SelectOption v-for="item in sourceOptions" :value="item.value">{{
                        item.label
                      }}</SelectOption>
                    </Select>
                  </FormItem>
                </Col>
                <Col :span="12">
                  <FormItem label="权重">
                    <Select
                      placeholder="请选择权重"
                      v-model:value="form.checkpointDir"
                      :loading="props.requestMap.modelWeightOptionsRequest.loading.value"
                      :dropdownStyle="selectDropStyle"
                    >
                      <SelectOption
                        v-for="item in modelDirOptions"
                        :value="item.value"
                        :title="item.label"
                      >
                        {{ item.label }}
                      </SelectOption>
                    </Select>
                  </FormItem>
                </Col>
              </Row>
              <Row :gutter="[10, 0]">
                <Col :span="24">
                  <FormItem label="任务描述">
                    <Textarea
                      v-model:value="form.description"
                      placeholder="请输入"
                      :autoSize="{
                        minRows: 1,
                        maxRows: 1,
                      }"
                    />
                  </FormItem>
                </Col>
              </Row>
              <!-- <Row :gutter="[10, 0]">
                <Col :span="24">
                  <FormItem>
                    <template #label>
                      <span> 选择配置文件</span>
                    </template>
                    <Select
                      placeholder="请选择配置文件"
                      v-model:value="form.scriptCommand"
                      :dropdownStyle="selectDropStyle"
                    >
                      <SelectOption v-for="item in scriptOptions" :value="item">
                        {{ item }}
                      </SelectOption>
                    </Select>

                  </FormItem>
                </Col>
              </Row> -->
            </Form>
          </Spin>
        </ModuleCard>
      </div>
      <div
        :class="{
          [`${prefix}-grid`]: true,
          [`${prefix}-grid--system`]: true,
        }"
      >
        <ModuleCard title="">
          <template #head-extra>
            <a-space :size="20">
              <Button
                v-if="createtaskVisible"
                type="primary"
                class="custom-btn"
                :loading="taskLoading"
                @click="onStart"
              >
                <template #icon>
                  <Icon name="stop" />
                </template>
                开始推理
              </Button>
              <Button
                v-if="interruptVisible"
                type="primary"
                danger
                class="custom-btn"
                :loading="taskLoading"
                @click="onStop"
              >
                <template #icon>
                  <Icon name="interruptLine" />
                </template>
                停止推理
              </Button>
            </a-space>
          </template>

          <div :class="`${prefix}-row`">
            <div :class="`${prefix}-grid--process`">
              <div :class="`${prefix}-grid`">
                <Spin :spinning="props.requestMap.taskStatusRequest.loading.value">
                  <div :class="`${prefix}-desc`">
                    推理进度
                    <div
                      :class="`${prefix}-desc-extra`"
                      :style="`background: ${statusColorMap[props.state.taskStatus.value.status]?.light}`"
                    >
                      {{ showStatus }}
                    </div>
                  </div>
                  <div :class="`${prefix}-wrapper`">
                    <Progress
                      :percent="showProgress"
                      :strokeColor="statusColorMap[props.state.taskStatus.value.status]?.dark"
                    />
                  </div>
                </Spin>
              </div>
              <div :class="`${prefix}-grid--log`" ref="logDomRef">
                <div
                  :class="{
                    [`${prefix}-desc`]: true,
                    [`${prefix}-desc--log`]: true,
                    [`${prefix}-desc--log--full`]: isFull,
                  }"
                >
                  推理日志
                  <Tooltip title="全屏">
                    <Button
                      size="small"
                      type="text"
                      class="custom-btn custom-btn--log"
                      @click="onLogZoomChange"
                    >
                      <template #icon>
                        <Icon name="zoomIn" />
                      </template>
                    </Button>
                  </Tooltip>
                </div>
                <div
                  :class="{
                    [`${prefix}-wrapper--log`]: true,
                    [`${prefix}-wrapper--log--full`]: isFull,
                  }"
                  ref="logContainerRef"
                >
                  <div :class="`${prefix}-log-row`" v-for="item in props.state.log.logs.value">
                    {{ item }}
                  </div>
                </div>
              </div>
            </div>

            <div
              :class="{
                [`${prefix}-grid`]: true,
                [`${prefix}-grid--source`]: true,
              }"
            >
              <div :class="`${prefix}-desc`">资源监控</div>
              <Spin :spinning="systemInfoLoading">
                <div :class="`${prefix}-wrapper--source`">
                  <div :class="`${prefix}-source-card`" v-for="item in cardList">
                    <div :class="`${prefix}-source-card-title`">
                      {{ item.label }}
                    </div>
                    <div :class="`${prefix}-source-card-value`">
                      {{ sourceFieldFormat(item.key) }}
                    </div>
                  </div>
                </div>
              </Spin>
            </div>
          </div>
        </ModuleCard>
      </div>
    </div>
  </div>
</template>

<style lang="scss" scoped>
$prefix: "model-reasoning";

.#{$prefix} {
  width: 100%;
  height: 100%;
  :deep(.ant-spin-nested-loading) {
    width: 100%;
    height: 100%;
  }
  &-wrapper {
    display: flex;
    align-items: start;
    justify-content: start;
    height: 100%;
    // gap: 65px;
    &--process {
      width: 100%;
    }
    &--source {
      display: flex;
      align-items: start;
      justify-content: start;
      flex-direction: column;
      gap: 11px;
      flex-wrap: wrap;
    }
    &--log {
      border-radius: 10px 10px 10px 10px;
      background: #dcdcdc;
      flex: 1;
      height: calc(100% - 24px);
      width: 100%;
      overflow: auto;
      padding: 13px 18px;
      box-sizing: border-box;
      &--full {
        border-radius: 0;
      }
    }
  }
  &-row {
    width: 100%;
    height: 100%;
    display: flex;
    align-items: start;
    align-self: stretch;
    &--system {
      flex-direction: column;
      width: 100%;
      height: 100%;
    }
  }
  &-grid {
    width: 100%;
    flex-shrink: 0;
    &--form {
      width: 34%;
      :deep(.ant-form-item) {
        margin-bottom: 0px;
        .ant-form-item-label {
          padding: 0;
          margin-bottom: 4px;
          label {
            font-family:
              Source Han Sans,
              Source Han Sans;
            font-weight: 350;
            font-size: 12px;
            color: #4b5563;
            line-height: 16px;
            text-align: left;
            font-style: normal;
            text-transform: none;
          }
        }
      }
    }
    &--system {
      width: 64.5%;
      height: 100%;
    }
    &--process {
      width: calc(100% - 120px - 22px);
      height: 100%;
      margin-right: 22px;
      display: flex;
      align-items: start;
      justify-content: start;
      flex-direction: column;
    }
    &--source {
      width: 120px;
    }
    &--log {
      flex: 1;
      align-self: stretch;
      overflow: hidden;
      //
    }
  }
  &-desc {
    width: 100%;
    font-family:
      Source Han Sans,
      Source Han Sans;
    font-weight: 350;
    font-size: 12px;
    color: #4b5563;
    line-height: 16px;
    text-align: left;
    font-style: normal;
    text-transform: none;
    margin-bottom: 8px;
    display: flex;
    align-items: start;
    &-extra {
      margin-left: auto;
      background: #bfdbfe;
      border-radius: 9999px;
      font-family:
        Source Han Sans,
        Source Han Sans;
      font-weight: 350;
      font-size: 12px;
      color: #000000;
      line-height: 16px;
      text-align: left;
      font-style: normal;
      text-transform: none;
      padding: 4px 6px;
    }
    &--log {
      padding-top: 4px;
      margin-bottom: 0;
      padding-bottom: 4px;
      &--full {
        background: rgba(255, 255, 255);
      }
    }
  }
  &-source-card {
    padding: 6px 12px 0;
    width: 100%;
    // width: calc((100% - 21px) / 2);
    border-radius: 16px;
    background: #fff;
    border-radius: 16px 16px 16px 16px;
    &-title {
      font-family:
        Source Han Sans,
        Source Han Sans;
      font-weight: 350;
      font-size: 12px;
      color: #4b5563;
      line-height: 16px;
      text-align: left;
      font-style: normal;
      text-transform: none;
      margin-bottom: 4px;
    }
    &-value {
      font-family:
        Source Han Sans,
        Source Han Sans;
      font-weight: 350;
      font-size: 18px;
      color: #000000;
      line-height: 28px;
      text-align: left;
      font-style: normal;
      text-transform: none;
    }
  }
  &-form {
    overflow: hidden;
  }
  .custom-btn {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 10px;
    border-radius: 4px 4px 4px 4px;
    padding: 8px 16px;
    &--log {
      height: 18px;
      line-height: 12px;
      font-size: 16px;
      margin-left: auto;
    }
  }
  &-log-row {
    margin-bottom: 5px;
    &:last-child {
      margin-bottom: 0px;
    }
  }
  .edit-btn {
    display: flex;
    align-items: center;
    justify-content: center;
    margin-left: 10px;
    cursor: pointer;
    color: #979797;
    &:hover {
      color: #2563eb;
    }
  }
}
</style>
<!-- <style lang="scss">
$prefix: "model-reasoning";

.#{$prefix} {
  &-form {
    .ant-form-item {
      margin-bottom: 0px;
    }
  }
  &-source-card {
    .ant-card {
      border: none;
      box-shadow: none;
      background: none;
    }
  }
}
</style> -->
