import KFIcon from '@/components/KFIcon';
import { AutoMLTaskType, AutoMLType, ExperimentStatus } from '@/enums';
import { getExperimentInsReq } from '@/services/autoML';
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 AutoMLBasic from '../components/AutoMLBasic';
import ExperimentHistory from '../components/ExperimentHistory';
import ExperimentLog from '../components/ExperimentLog';
import ExperimentResult from '../components/ExperimentResult';
import TensorBoard from '../components/TensorBoard';
import { AutoMLData, AutoMLInstanceData } from '../types';
import styles from './index.less';

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

const NodePrefix = 'workflow';

function AutoMLInstance() {
  const [autoMLInfo, setAutoMLInfo] = useState<AutoMLData | undefined>(undefined);
  const [instanceInfo, setInstanceInfo] = useState<AutoMLInstanceData | undefined>(undefined);
  const [workflowStatus, setWorkflowStatus] = useState<NodeStatus | undefined>(undefined);
  const [nodes, setNodes] = useState<Record<string, NodeStatus> | undefined>(undefined);
  const [type, setType] = useState<string | 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(getExperimentInsReq(instanceId));
    if (res && res.data) {
      const info = res.data as AutoMLInstanceData;
      const { param, node_status, argo_ins_name, argo_ins_ns, status, create_time, type } = info;

      setType(type);
      // 解析配置参数
      const paramJson = parseJsonText(param);
      if (paramJson) {
        setAutoMLInfo({
          ...paramJson.data,
          create_time,
          type,
        });
      }

      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) {
          const workflowStatus = Object.values(nodes).find((node: any) =>
            node.displayName.startsWith(NodePrefix),
          ) as NodeStatus;

          // 节点
          setNodes(nodes);

          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: (
        <AutoMLBasic
          className={styles['auto-ml-instance__basic']}
          info={autoMLInfo}
          runStatus={workflowStatus}
          isInstance
        />
      ),
    },
    {
      key: TabKeys.Log,
      label: '日志',
      icon: <KFIcon type="icon-rizhi1" />,
      children: (
        <div className={styles['auto-ml-instance__log']}>
          {instanceInfo && nodes && <ExperimentLog instanceInfo={instanceInfo} nodes={nodes} />}
        </div>
      ),
    },
  ];

  const resultTabItems = [
    {
      key: TabKeys.Result,
      label: '实验结果',
      icon: <KFIcon type="icon-shiyanjieguo1" />,
      children: (
        <ExperimentResult
          type={type}
          fileUrl={instanceInfo?.result_path}
          imageUrl={instanceInfo?.img_path}
          modelPath={instanceInfo?.model_path}
        />
      ),
    },
    type === AutoMLType.Text
      ? {
          key: TabKeys.Visual,
          label: '可视化结果',
          icon: <KFIcon type="icon-Trialliebiao" />,
          children: (
            <TensorBoard
              path={instanceInfo?.run_history_path}
              namespace={instanceInfo?.argo_ins_ns}
            />
          ),
        }
      : {
          key: TabKeys.History,
          label: '试验列表',
          icon: <KFIcon type="icon-Trialliebiao" />,
          children: (
            <ExperimentHistory
              calcMetrics={autoMLInfo?.scoring_functions}
              fileUrl={instanceInfo?.run_history_path}
              isClassification={autoMLInfo?.task_type === AutoMLTaskType.Classification}
            />
          ),
        },
  ];

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

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

export default AutoMLInstance;
