import KFIcon from '@/components/KFIcon';
import { ExperimentStatus } from '@/enums';
import { getRayInsReq } from '@/services/hyperParameter';
import { NodeStatus } from '@/types';
import { parseJsonText } from '@/utils';
import { safeInvoke } from '@/utils/functional';
import { to } from '@/utils/promise';
import { useParams } from '@umijs/max';
import { Tabs } from 'antd';
import { useEffect, useRef, useState } from 'react';
import ExperimentHistory from '../components/ExperimentHistory';
import ExperimentLog from '../components/ExperimentLog';
import ExperimentResult from '../components/ExperimentResult';
import HyperParameterBasic from '../components/HyperParameterBasic';
import { HyperParameterData, HyperParameterInstanceData } from '../types';
import styles from './index.less';

enum TabKeys {
  Params = 'params',
  Log = 'log',
  Result = 'result',
  History = 'history',
}

const NodePrefix = 'workflow';

function HyperParameterInstance() {
  const [experimentInfo, setExperimentInfo] = useState<HyperParameterData | undefined>(undefined);
  const [instanceInfo, setInstanceInfo] = useState<HyperParameterInstanceData | undefined>(
    undefined,
  );
  // 超参数寻优运行有3个节点，运行状态取工作流状态，而不是 auto-hpo 节点状态
  const [workflowStatus, setWorkflowStatus] = useState<NodeStatus | undefined>(undefined);
  const [nodes, setNodes] = useState<Record<string, NodeStatus> | undefined>(undefined);
  const params = useParams();
  const instanceId = safeInvoke(Number)(params.id);
  const evtSourceRef = useRef<EventSource | null>(null);

  useEffect(() => {
    if (instanceId) {
      getExperimentInsInfo(false);
    }
    return () => {
      closeSSE();
    };
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [instanceId]);

  // 获取实验实例详情
  const getExperimentInsInfo = async (isStatusDetermined: boolean) => {
    const [res] = await to(getRayInsReq(instanceId));
    if (res && res.data) {
      const info = res.data as HyperParameterInstanceData;
      const { param, node_status, argo_ins_name, argo_ins_ns, status } = info;
      // 解析配置参数
      const paramJson = parseJsonText(param).data;
      if (paramJson) {
        // 实例详情返回的参数是字符串，需要转换
        if (typeof paramJson.parameters === 'string') {
          paramJson.parameters = parseJsonText(paramJson.parameters);
        }
        if (!Array.isArray(paramJson.parameters)) {
          paramJson.parameters = [];
        }

        // 实例详情返回的运行参数是字符串，需要转换
        if (typeof paramJson.points_to_evaluate === 'string') {
          paramJson.points_to_evaluate = parseJsonText(paramJson.points_to_evaluate);
        }
        if (!Array.isArray(paramJson.points_to_evaluate)) {
          paramJson.points_to_evaluate = [];
        }
        setExperimentInfo({
          ...paramJson,
        });
      }

      setInstanceInfo(info);

      // 这个接口返回的状态有延时，SSE 返回的状态是最新的
      // SSE 调用时，不需要解析 node_status，也不要重新建立 SSE
      if (isStatusDetermined) {
        return;
      }

      // 进行节点状态
      const nodeStatusJson = parseJsonText(node_status);
      if (nodeStatusJson) {
        setNodes(nodeStatusJson);
        Object.keys(nodeStatusJson).some((key) => {
          if (key.startsWith(NodePrefix)) {
            const workflowStatus = nodeStatusJson[key];
            setWorkflowStatus(workflowStatus);
            return true;
          }
          return false;
        });
      }

      // 运行中或者等待中，开启 SSE
      if (status === ExperimentStatus.Pending || status === ExperimentStatus.Running) {
        setupSSE(argo_ins_name, argo_ins_ns);
      }
    }
  };

  const setupSSE = (name: string, namespace: string) => {
    const { origin } = location;
    const params = encodeURIComponent(`metadata.namespace=${namespace},metadata.name=${name}`);
    const evtSource = new EventSource(
      `${origin}/api/v1/realtimeStatus?listOptions.fieldSelector=${params}`,
      { withCredentials: false },
    );
    evtSource.onmessage = (event) => {
      const data = event?.data;
      if (!data) {
        return;
      }
      const dataJson = parseJsonText(data);
      if (dataJson) {
        const nodes = dataJson?.result?.object?.status?.nodes;
        if (nodes) {
          // 设置节点
          setNodes(nodes);

          // 设置总 workflow 状态
          const workflowStatus = Object.values(nodes).find((node: any) =>
            node.displayName.startsWith(NodePrefix),
          ) as NodeStatus;
          if (workflowStatus) {
            setWorkflowStatus(workflowStatus);

            // 实验结束，关闭 SSE，获取实验实例结果
            if (
              workflowStatus.phase !== ExperimentStatus.Pending &&
              workflowStatus.phase !== ExperimentStatus.Running
            ) {
              closeSSE();
              getExperimentInsInfo(true);
            }
          }
        }
      }
    };
    evtSource.onerror = (error) => {
      console.error('SSE error: ', error);
    };

    evtSourceRef.current = evtSource;
  };

  const closeSSE = () => {
    if (evtSourceRef.current) {
      evtSourceRef.current.close();
      evtSourceRef.current = null;
    }
  };

  const basicTabItems = [
    {
      key: TabKeys.Params,
      label: '基本信息',
      icon: <KFIcon type="icon-jibenxinxi" />,
      children: (
        <HyperParameterBasic
          className={styles['hyper-parameter-instance__basic']}
          info={experimentInfo}
          workflowStatus={workflowStatus}
          instanceStatus={instanceInfo?.status as ExperimentStatus}
          isInstance
        />
      ),
    },
    {
      key: TabKeys.Log,
      label: '日志',
      icon: <KFIcon type="icon-rizhi1" />,
      children: (
        <div className={styles['hyper-parameter-instance__log']}>
          {instanceInfo && nodes && <ExperimentLog instanceInfo={instanceInfo} nodes={nodes} />}
        </div>
      ),
    },
  ];

  const resultTabItems = [
    {
      key: TabKeys.Result,
      label: '实验结果',
      icon: <KFIcon type="icon-shiyanjieguo1" />,
      children: <ExperimentResult fileUrl={instanceInfo?.result_txt} />,
    },
    {
      key: TabKeys.History,
      label: '寻优列表',
      icon: <KFIcon type="icon-Trialliebiao" />,
      children: <ExperimentHistory trialList={instanceInfo?.trial_list ?? []} />,
    },
  ];

  const tabItems =
    instanceInfo?.status === ExperimentStatus.Succeeded
      ? [...basicTabItems, ...resultTabItems]
      : basicTabItems;

  return (
    <div className={styles['hyper-parameter-instance']}>
      <Tabs className={styles['hyper-parameter-instance__tabs']} items={tabItems} />
    </div>
  );
}

export default HyperParameterInstance;
