import KFIcon from '@/components/KFIcon';
import PageTitle from '@/components/PageTitle';
import { ExperimentStatus, TensorBoardStatus } from '@/enums';
import { useCacheState } from '@/hooks/useCacheState';
import { useServerTime } from '@/hooks/useServerTime';
import {
  deleteExperimentById,
  editExperimentInsReq,
  getExperiment,
  getExperimentById,
  getQueryByExperimentId,
  getTensorBoardStatusReq,
  postExperiment,
  putExperiment,
  runExperiments,
  runTensorBoardReq,
} from '@/services/experiment/index.js';
import { getWorkflow } from '@/services/pipeline/index.js';
import themes from '@/styles/theme.less';
import { ExperimentCompleted } from '@/utils/constant';
import { to } from '@/utils/promise';
import SessionStorage from '@/utils/sessionStorage';
import tableCellRender, { TableCellValueType } from '@/utils/table';
import { modalConfirm } from '@/utils/ui';
import { App, Button, ConfigProvider, Dropdown, Input, Space, Table, Tooltip } from 'antd';
import classNames from 'classnames';
import { useCallback, useEffect, useRef, useState } from 'react';
import { useNavigate } from 'react-router-dom';
import { ComparisonType } from './Comparison/config';
import AddExperimentModal from './components/AddExperimentModal';
import ExperimentInstanceList from './components/ExperimentInstanceList';
import styles from './index.less';
import { experimentStatusInfo } from './status';

// 定时器
const timerIds = new Map();

function Experiment() {
  const navigate = useNavigate();
  const [experimentList, setExperimentList] = useState([]);
  const [workflowList, setWorkflowList] = useState([]);
  const [experimentId, setExperimentId] = useState(null);
  const [experimentInsList, setExperimentInsList] = useState([]);
  const [expandedRowKeys, setExpandedRowKeys] = useState([]);
  const [total, setTotal] = useState(0);
  const [isAdd, setIsAdd] = useState(true);
  const [isModalOpen, setIsModalOpen] = useState(false);
  const [addFormData, setAddFormData] = useState({});
  const [experimentInsTotal, setExperimentInsTotal] = useState(0);
  const [cacheState, setCacheState] = useCacheState();
  const [searchText, setSearchText] = useState(cacheState?.searchText);
  const [inputText, setInputText] = useState(cacheState?.searchText);
  const [now] = useServerTime();
  const [pagination, setPagination] = useState(
    cacheState?.pagination ?? {
      current: 1,
      pageSize: 10,
    },
  );
  const { message } = App.useApp();
  const timerRef = useRef();

  // 获取实验列表
  const getExperimentList = useCallback(
    async (skipLoading = false) => {
      const params = {
        page: pagination.current - 1,
        size: pagination.pageSize,
        name: searchText || undefined,
      };
      const [res] = await to(getExperiment(params, skipLoading));
      if (res && res.data && Array.isArray(res.data.content)) {
        setExperimentList(
          res.data.content.map((item) => {
            return { ...item, key: item.id };
          }),
        );

        setTotal(res.data.totalElements);
      }
    },
    [pagination, searchText],
  );

  // 刷新实验列表状态，
  // 目前是直接刷新实验列表，后续需要优化，只刷新状态
  const refreshExperimentList = useCallback(
    (skipLoading = false) => {
      getExperimentList(skipLoading);
    },
    [getExperimentList],
  );

  // 获取 TensorBoard 状态
  const getTensorBoardStatus = useCallback(async (experimentIn) => {
    const params = {
      namespace: experimentIn.nodes_result.tensorboard_log.namespace,
      path: experimentIn.nodes_result.tensorboard_log.path,
      pvc_name: experimentIn.nodes_result.tensorboard_log.pvc_name,
    };
    const [res] = await to(getTensorBoardStatusReq(params));
    if (res && res.data) {
      setExperimentInsList((prevList) => {
        return prevList.map((item) => {
          if (item.id === experimentIn.id) {
            return {
              ...item,
              tensorBoardStatus: res.data.status,
              tensorboardUrl: res.data.url,
            };
          }
          return item;
        });
      });

      let timerId = timerIds.get(experimentIn.id);
      if (timerId) {
        clearTimeout(timerId);
        timerIds.delete(experimentIn.id);
      }
      timerId = setTimeout(() => {
        getTensorBoardStatus(experimentIn);
      }, 10 * 1000);
      timerIds.set(experimentIn.id, timerId);
    }
  }, []);

  // 获取实验实例列表
  const getExperimentInsList = useCallback(
    async (experimentId, page, size = 5, skipLoading = false) => {
      const params = {
        experimentId: experimentId,
        page: page,
        size: size,
      };
      const [res, error] = await to(getQueryByExperimentId(params, skipLoading));
      if (res && res.data) {
        const { content = [], totalElements = 0 } = res.data;
        try {
          const list = content.map((v) => {
            const nodes_result = v.nodes_result ? JSON.parse(v.nodes_result) : {};
            return {
              ...v,
              nodes_result,
            };
          });
          if (page === 0) {
            setExperimentInsList(list);
            clearExperimentInTimers();
          } else {
            setExperimentInsList((prev) => [...prev, ...list]);
          }
          setExperimentInsTotal(totalElements);
          // 获取 TensorBoard 状态
          list.forEach((item) => {
            if (item.nodes_result?.tensorboard_log) {
              getTensorBoardStatus(item);
            }
          });
        } catch (error) {
          console.error('JSON parse error: ', error);
        }
      }
    },
    [getTensorBoardStatus],
  );

  // 刷新实验实例列表
  const refreshExperimentIns = useCallback(
    (experimentId, skipLoading = false) => {
      const length = experimentInsList.length;
      getExperimentInsList(experimentId, 0, length, skipLoading);
    },
    [experimentInsList, getExperimentInsList],
  );

  // 更新实验状态
  const editExperimentIns = useCallback(
    async (experimentId, experimentInsId, status, argo_ins_name, argo_ins_ns) => {
      const params = {
        experiment_id: experimentId,
        id: experimentInsId,
        status: status,
        argo_ins_name,
        argo_ins_ns,
      };
      const [res, error] = await to(editExperimentInsReq(params));
      if (res && res.data) {
        refreshExperimentIns(experimentId, true);
        refreshExperimentList(true);
      }
    },
    [refreshExperimentIns, refreshExperimentList],
  );

  // 获取流水线列表
  useEffect(() => {
    // 获取流水线列表
    const getWorkflowList = async () => {
      const queryFlow = {
        offset: 1,
        page: 0,
        size: 10000,
        name: null,
      };
      const [res] = await to(getWorkflow(queryFlow));
      if (res && res.data && res.data.content) {
        setWorkflowList(res.data.content);
      }
    };

    getWorkflowList();
    return () => {
      clearExperimentInTimers();
    };
  }, []);
  // 获取实验列表
  useEffect(() => {
    getExperimentList();
  }, [getExperimentList]);

  // 更新实验实例状态
  useEffect(() => {
    const handleMessage = (e) => {
      const { type, payload } = e.data;
      if (type === ExperimentCompleted) {
        const { experimentId, experimentInsId, status, finishTime } = payload;
        const currentIns = experimentInsList.find((v) => v.id === experimentInsId);
        console.log(
          '实验实例状态变化',
          currentIns?.status,
          status,
          experimentId,
          experimentInsId,
          finishTime,
        );

        if (
          !currentIns ||
          currentIns.status === ExperimentStatus.Terminated ||
          currentIns.status === status
        ) {
          return;
        }

        editExperimentIns(
          experimentId,
          experimentInsId,
          status,
          currentIns.argo_ins_name,
          currentIns.argo_ins_ns,
        );

        // refreshExperimentList(true);
        // refreshExperimentIns(experimentId);

        // 修改实例的状态和结束时间
        // setExperimentInsList((prev) =>
        //   prev.map((v) =>
        //     v.id === experimentInsId
        //       ? {
        //           ...v,
        //           status: status,
        //           finish_time: finishTime,
        //         }
        //       : v,
        //   ),
        // );
      }
    };

    window.addEventListener('message', handleMessage);
    return () => {
      window.removeEventListener('message', handleMessage);
    };
  }, [experimentInsList, editExperimentIns]);

  // 搜索
  const onSearch = (value) => {
    setSearchText(value);
    setPagination((prev) => ({
      ...prev,
      current: 1,
    }));
  };

  // 运行 TensorBoard
  const runTensorBoard = async (experimentIn) => {
    const params = {
      namespace: experimentIn.nodes_result.tensorboard_log.namespace,
      path: experimentIn.nodes_result.tensorboard_log.path,
      pvc_name: experimentIn.nodes_result.tensorboard_log.pvc_name,
    };
    const [res] = await to(runTensorBoardReq(params));
    if (res) {
      experimentIn.tensorboardUrl = res.data;
      const timerId = timerIds.get(experimentIn.id);
      if (timerId) {
        clearTimeout(timerId);
        timerIds.delete(experimentIn.id);
        getTensorBoardStatus(experimentIn);
      }
    }
  };

  // 展开实例
  const expandChange = (expanded, record) => {
    clearExperimentInTimers();
    setExperimentInsList([]);
    if (expanded) {
      setExpandedRowKeys([record.id]);
      getExperimentInsList(record.id, 0, 5);
      refreshExperimentList();
    } else {
      setExpandedRowKeys([]);
    }
  };

  // 终止实验实例获取 TensorBoard 状态的定时器
  const clearExperimentInTimers = () => {
    timerIds.values().forEach((timerId) => {
      clearTimeout(timerId);
    });
    timerIds.clear();
  };

  // 创建实验
  const createExperiment = () => {
    setIsAdd(true);
    setAddFormData({});
    setExperimentId(null);
    setIsModalOpen(true);
  };

  // 编辑实验
  const editExperiment = (id) => {
    getExperimentById(id).then((res) => {
      setAddFormData({
        ...res.data,
      });
      setExperimentId(res.data.id);
      setIsAdd(false);
      setIsModalOpen(true);
    });
  };
  // 创建或编辑实验取消
  const handleCancel = () => {
    setIsModalOpen(false);
  };

  // 创建或者编辑实验
  const handleAddExperiment = async (values, isRun) => {
    const global_param = JSON.stringify(values.global_param);
    if (!experimentId) {
      const params = {
        ...values,
        global_param,
      };
      const [res] = await to(postExperiment(params));
      if (res) {
        message.success('新建实验成功');
        setIsModalOpen(false);
        getExperimentList();
      }
    } else {
      const params = { ...values, global_param, id: experimentId };
      const [res] = await to(putExperiment(params));
      if (res) {
        message.success('编辑实验成功');
        setIsModalOpen(false);
        getExperimentList();

        // 确定并运行
        if (isRun) {
          runExperiment(experimentId);
        }
      }
    }
  };

  // 当前页面切换
  const paginationChange = async (current, pageSize) => {
    setPagination({
      current,
      pageSize,
    });
  };
  // 运行实验
  const runExperiment = async (id) => {
    const [res] = await to(runExperiments(id));
    if (res) {
      message.success('运行成功');
      setExpandedRowKeys([id]);
      refreshExperimentList();
      getExperimentInsList(id, 0, 5);
    }
  };

  // 跳转, 缓存当前状态
  const navigateToUrl = (url) => {
    setCacheState({
      pagination,
      searchText,
    });
    navigate(url);
  };

  // 跳转到流水线
  const gotoPipeline = (record) => {
    navigateToUrl(`/pipeline/template/info/${record.workflow_id}`);
  };

  // 跳转到实验实例详情
  const gotoInstanceInfo = (item, record) => {
    navigateToUrl(`/pipeline/experiment/instance/${record.workflow_id}/${item.id}`);
  };

  // 处理 TensorBoard 操作
  const handleTensorboard = async (experimentIn) => {
    if (
      experimentIn.tensorBoardStatus === TensorBoardStatus.Terminated ||
      experimentIn.tensorBoardStatus === TensorBoardStatus.Failed
    ) {
      await runTensorBoard(experimentIn);
    } else if (
      experimentIn.tensorBoardStatus === TensorBoardStatus.Running &&
      experimentIn.tensorboardUrl
    ) {
      const url = experimentIn.tensorboardUrl;
      SessionStorage.setItem(SessionStorage.tensorBoardUrlKey, url);
      navigateToUrl(`/pipeline/experiment/visual`);
      // window.open(experimentIn.tensorboardUrl, '_blank');
    }
  };

  // 实验实例终止
  const handleInstanceTerminate = async (experimentIn) => {
    // 修改实例的状态和结束时间
    setExperimentInsList((prevList) => {
      return prevList.map((item) => {
        if (item.id === experimentIn.id) {
          return {
            ...item,
            status: ExperimentStatus.Terminated,
            finish_time: now().toISOString(),
          };
        }
        return item;
      });
    });
    // 刷新实验列表和实例列表
    refreshExperimentList(true);
    refreshExperimentIns(experimentIn.experiment_id);
  };

  // 实验对比菜单
  const getComparisonMenu = (experimentId) => {
    return {
      items: [
        {
          label: <span>训练对比</span>,
          key: ComparisonType.Train,
        },
        {
          label: <span>评估对比</span>,
          key: ComparisonType.Evaluate,
        },
      ],
      onClick: ({ key }) => {
        navigateToUrl(`/pipeline/experiment/compare?type=${key}&id=${experimentId}`);
      },
    };
  };

  // 加载更多实验实例
  const loadMoreExperimentIns = () => {
    const page = Math.round(experimentInsList.length / 5);
    getExperimentInsList(expandedRowKeys[0], page, 5);
  };

  // 处理删除
  const handleExperimentDelete = (record) => {
    modalConfirm({
      title: '删除后，该实验将不可恢复',
      content: '是否确认删除?',
      onOk: () => {
        deleteExperimentById(record.id).then((ret) => {
          if (ret.code === 200) {
            message.success('删除成功');
            // 如果是一页的唯一数据，删除后，请求第一页的数据
            // 否则直接刷新这一页的数据
            setPagination((prev) => {
              return {
                ...prev,
                current: experimentList.length === 1 ? Math.max(1, prev.current - 1) : prev.current,
              };
            });
          } else {
            message.error(ret.msg);
          }
        });
      },
    });
  };

  const columns = [
    {
      title: '实验名称',
      dataIndex: 'name',
      key: 'name',
      render: tableCellRender(false),
      width: '16%',
    },
    {
      title: '关联流水线名称',
      dataIndex: 'workflow_name',
      key: 'workflow_name',
      render: tableCellRender(false, TableCellValueType.Link, {
        onClick: gotoPipeline,
      }),
      width: '16%',
    },
    {
      title: '实验描述',
      dataIndex: 'description',
      key: 'description',
      render: tableCellRender(true),
    },
    {
      title: '最近五次运行状态',
      dataIndex: 'status_list',
      key: 'status_list',
      width: 160,
      render: (text) => {
        const newText = text && text.replace(/\s+/g, '').split(',');
        return (
          <>
            {newText && newText.length > 0
              ? newText.map((item, index) => {
                  return (
                    <Tooltip key={index} placement="top" title={experimentStatusInfo[item].label}>
                      <img
                        style={{ width: '17px', marginRight: '6px' }}
                        src={experimentStatusInfo[item].icon}
                        draggable={false}
                        alt=""
                      />
                    </Tooltip>
                  );
                })
              : null}
          </>
        );
      },
    },
    {
      title: '操作',
      key: 'action',
      width: 360,
      render: (_, record) => (
        <Space size="small">
          <Button
            type="link"
            size="small"
            key="run"
            icon={<KFIcon type="icon-yunhang" />}
            onClick={() => {
              runExperiment(record.id);
            }}
          >
            运行
          </Button>
          <Button
            type="link"
            size="small"
            key="edit"
            icon={<KFIcon type="icon-bianji" />}
            onClick={() => {
              editExperiment(record.id);
            }}
          >
            编辑
          </Button>
          <Dropdown key="comparison" menu={getComparisonMenu(record.id)}>
            <a onClick={(e) => e.preventDefault()}>
              <Space style={{ padding: '0 7px' }}>
                <KFIcon type="icon-shiyanduibi" />
                实验对比
              </Space>
            </a>
          </Dropdown>
          <ConfigProvider
            theme={{
              token: {
                colorLink: themes['warningColor'],
              },
            }}
          >
            <Button
              type="link"
              size="small"
              key="batchRemove"
              icon={<KFIcon type="icon-shanchu" />}
              onClick={() => handleExperimentDelete(record)}
            >
              删除
            </Button>
          </ConfigProvider>
        </Space>
      ),
    },
  ];

  return (
    <div className={styles['experiment-list']}>
      <PageTitle title="实验列表"></PageTitle>
      <div className={styles['experiment-list__content']}>
        <div className={styles['experiment-list__content__filter']}>
          <Input.Search
            placeholder="按实验名称筛选"
            onSearch={onSearch}
            onChange={(e) => setInputText(e.target.value)}
            style={{ width: 300 }}
            value={inputText}
            allowClear
          />
          <Button type="default" onClick={createExperiment} icon={<KFIcon type="icon-xinjian2" />}>
            新建实验
          </Button>
        </div>
        <div
          className={classNames('vertical-scroll-table', styles['experiment-list__content__table'])}
        >
          <Table
            columns={columns}
            dataSource={experimentList}
            pagination={{
              ...pagination,
              total: total,
              showSizeChanger: true,
              showQuickJumper: true,
              showTotal: () => `共${total}条`,
              onChange: paginationChange,
            }}
            rowKey="id"
            scroll={{ y: 'calc(100% - 55px)' }}
            expandable={{
              expandedRowRender: (record) => (
                <ExperimentInstanceList
                  experimentInsList={experimentInsList}
                  experimentInsTotal={experimentInsTotal}
                  onClickInstance={(item) => gotoInstanceInfo(item, record)}
                  onClickTensorBoard={handleTensorboard}
                  onRemove={() => {
                    refreshExperimentIns(record.id);
                    refreshExperimentList();
                  }}
                  onTerminate={handleInstanceTerminate}
                  onLoadMore={() => loadMoreExperimentIns()}
                ></ExperimentInstanceList>
              ),
              onExpand: expandChange,
              expandedRowKeys: expandedRowKeys,
            }}
          />
        </div>
      </div>

      {isModalOpen && (
        <AddExperimentModal
          isAdd={isAdd}
          open={isModalOpen}
          initialValues={addFormData}
          onCancel={handleCancel}
          onFinish={handleAddExperiment}
          workflowList={workflowList}
        />
      )}
    </div>
  );
}
export default Experiment;
