/**
 * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
 * SPDX-License-Identifier: MIT
 */

import { FC, useContext, useEffect, useRef, useState } from 'react';

import classnames from 'classnames';
import { useService, I18n } from '@flowgram.ai/free-layout-editor';
import { Button, SideSheet, Switch, Tag } from '@douyinfe/semi-ui';
import { IconClose, IconPlay, IconSpin } from '@douyinfe/semi-icons';

import { TestRunJsonInput } from '../testrun-json-input';
import { TestRunForm } from '../testrun-form';
import { NodeStatusGroup } from '../node-status-bar/group';
// 改为使用后端运行服务
import { CustomService } from '../../../services';
import { SidebarContext } from '../../../context';
import { IconCancel } from '../../../assets/icon-cancel';

import styles from './index.module.less';

interface TestRunSidePanelProps {
  visible: boolean;
  onCancel: () => void;
}

export const TestRunSidePanel: FC<TestRunSidePanelProps> = ({ visible, onCancel }) => {
  const customService = useService(CustomService);
  const { nodeId: sidebarNodeId, setNodeId } = useContext(SidebarContext);

  const [isRunning, setRunning] = useState(false);
  const [values, setValues] = useState<Record<string, unknown>>({});
  const [errors, setErrors] = useState<string[]>();
  const [result, setResult] = useState<
    | {
        ok?: boolean;
        ctx: any;
        logs: string[];
      }
    | undefined
  >();

  // 模式切换：SSE 流式 or 普通 HTTP
  const [streamMode, _setStreamMode] = useState<boolean>(() => {
    const saved = localStorage.getItem('testrun-stream-mode');
    return saved ? JSON.parse(saved) : true;
  });
  const setStreamMode = (checked: boolean) => {
    _setStreamMode(checked);
    localStorage.setItem('testrun-stream-mode', JSON.stringify(checked));
  };
  // 当启用流式时，选择 WS 或 SSE（默认 SSE）
  const [useWS, _setUseWS] = useState<boolean>(() => {
    const saved = localStorage.getItem('testrun-ws-mode');
    return saved ? JSON.parse(saved) : false;
  });
  const setUseWS = (checked: boolean) => {
    _setUseWS(checked);
    localStorage.setItem('testrun-ws-mode', JSON.stringify(checked));
  };

  // 流式渲染：实时上下文与日志
  const [streamCtx, setStreamCtx] = useState<any | undefined>();
  const [streamLogs, setStreamLogs] = useState<string[]>([]);
  const cancelRef = useRef<(() => void) | null>(null);

  // en - Use localStorage to persist the JSON mode state
  const [inputJSONMode, _setInputJSONMode] = useState(() => {
    const savedMode = localStorage.getItem('testrun-input-json-mode');
    return savedMode ? JSON.parse(savedMode) : false;
  });

  const setInputJSONMode = (checked: boolean) => {
    _setInputJSONMode(checked);
    localStorage.setItem('testrun-input-json-mode', JSON.stringify(checked));
  };

  const extractErrorMsg = (logs: string[] | undefined): string | undefined => {
    if (!logs || logs.length === 0) return undefined;
    const patterns = [/failed/i, /error/i, /panic/i];
    for (const line of logs) {
      if (patterns.some((p) => p.test(line))) return line;
    }
    return logs[logs.length - 1];
  };

  const onTestRun = async () => {
    if (isRunning) {
      // 运行中，忽略重复点击
      return;
    }
    setResult(undefined);
    setErrors(undefined);
    setStreamCtx(undefined);
    setStreamLogs([]);
    setRunning(true);
    try {
      // 运行前保存（静默），确保后端 YAML 与编辑器一致；若保存失败则不继续运行
      const saved = await customService.save({ silent: true });
      if (!saved) {
        setErrors([I18n.t('Save failed, cannot run')]);
        return;
      }

      if (streamMode) {
        const startStream = () => useWS
          ? customService.runStreamWS(values, {
              onNode: (evt) => {
                if (evt.ctx) setStreamCtx((prev: any) => ({ ...(prev || {}), ...(evt.ctx || {}) }));
                if (evt.logs && evt.logs.length) {
                  const normalizeLog = (s: string) => s.replace(/\r/g, '').trim();
                  const dedupLogs = (arr: string[]) => {
                    const seen = new Set<string>();
                    const res: string[] = [];
                    for (const s of arr) {
                      const key = normalizeLog(s);
                      if (!seen.has(key)) {
                        seen.add(key);
                        res.push(s);
                      }
                    }
                    return res;
                  };
                  const incoming = evt.logs!;
                  setStreamLogs((prev) => dedupLogs([...(prev || []), ...incoming]));
                }
              },
              onError: (evt) => {
                const msg = evt.message || I18n.t('Run failed');
                setErrors((prev) => [...(prev || []), msg]);
              },
              onDone: (evt) => {
                setResult({ ok: evt.ok, ctx: evt.ctx, logs: evt.logs });
              },
              onFatal: (err) => {
                setErrors((prev) => [...(prev || []), err.message || String(err)]);
                setRunning(false);
              },
            })
          : customService.runStream(values, {
              onNode: (evt) => {
                if (evt.ctx) setStreamCtx((prev: any) => ({ ...(prev || {}), ...(evt.ctx || {}) }));
                if (evt.logs && evt.logs.length) setStreamLogs((prev: string[]) => [...prev, ...evt.logs!]);
              },
              onError: (evt) => {
                const msg = evt.message || I18n.t('Run failed');
                setErrors((prev) => [...(prev || []), msg]);
              },
              onDone: (evt) => {
                setResult({ ok: evt.ok, ctx: evt.ctx, logs: evt.logs });
              },
              onFatal: (err) => {
                setErrors((prev) => [...(prev || []), err.message || String(err)]);
                setRunning(false);
              },
            });

        const { cancel, done } = startStream();

        cancelRef.current = cancel;

        const finished = await done;
        if (finished) {
          setResult(finished as any);
        } else {
          // 流结束但未收到 done 事件，给出提示
          setErrors((prev) => [...(prev || []), I18n.t('Stream terminated without completion')]);
        }
      } else {
        // 普通 HTTP 一次性运行
        try {
          const runRes = await customService.run(values);
          if (runRes) {
            if ((runRes as any).ok === false) {
              setResult(runRes as any);
              const err = extractErrorMsg((runRes as any).logs) || I18n.t('Run failed');
              setErrors([err]);
            } else {
              setResult(runRes as any);
            }
          } else {
            setErrors([I18n.t('Run failed')]);
          }
        } catch (e: any) {
          setErrors([e?.message || I18n.t('Run failed')]);
        }
      }
    } catch (e: any) {
      setErrors([e?.message || I18n.t('Run failed')]);
    } finally {
      setRunning(false);
      cancelRef.current = null;
    }
  };

  const onCancelRun = () => {
    try { cancelRef.current?.(); } catch {}
    setRunning(false);
  };

  const onClose = async () => {
    setValues({});
    if (isRunning) {
      if (streamMode) onCancelRun();
    }
    setRunning(false);
    setStreamCtx(undefined);
    setStreamLogs([]);
    onCancel();
  };

  // 当测试运行面板打开时，自动关闭右侧节点编辑侧栏，避免两个 SideSheet 重叠
  useEffect(() => {
    if (visible) {
      setNodeId(undefined);
    }
  }, [visible]);

  useEffect(() => {
    if (sidebarNodeId) {
      onCancel();
    }
  }, [sidebarNodeId]);

  const renderRunning = (
    <div className={styles['testrun-panel-running']}> 
      <IconSpin spin size="large" />
      <div className={styles.text}>{I18n.t('Running...')}</div>
      {/* 实时输出（仅流式模式显示） */}
      {streamMode && (
        <>
          {errors?.length ? (
            <div className={styles.error}>
              {errors.map((e) => (
                <div key={e}>{e}</div>
              ))}
            </div>
          ) : null}
          <NodeStatusGroup title={I18n.t('Context') + ' (Live)'} data={streamCtx} optional disableCollapse />
          <NodeStatusGroup title={I18n.t('Logs') + ' (Live)'} data={streamLogs} optional disableCollapse />
        </>
      )}
    </div>
  );

  const renderStatus = (
    <div style={{ marginBottom: 8 }}>
      {result?.ok === true && <Tag color="green">{I18n.t('Success')}</Tag>}
      {(errors?.length || result?.ok === false) && (
        <Tag color="red">{I18n.t('Failed')}</Tag>
      )}
    </div>
  );

  const renderForm = (
    <div className={styles['testrun-panel-form']}>
      <div className={styles['testrun-panel-input']}>
        <div className={styles.title}>{I18n.t('Input Form')}</div>
        <div className={styles.toggle}>
          <div>{I18n.t('JSON Mode')}</div>
          <Switch
            checked={inputJSONMode}
            onChange={(checked: boolean) => setInputJSONMode(checked)}
            size="small"
          />
        </div>
        <div className={styles.toggle}>
          <div>{I18n.t('Streaming Mode')}</div>
          <Switch
            checked={streamMode}
            onChange={(checked: boolean) => setStreamMode(checked)}
            size="small"
          />
        </div>
        {streamMode && (
          <div className={styles.toggle}>
            <div>WS</div>
            <Switch
              checked={useWS}
              onChange={(checked: boolean) => setUseWS(checked)}
              size="small"
            />
          </div>
        )}
      </div>
      {renderStatus}
      {errors?.map((e) => (
        <div className={styles.error} key={e}>
          {e}
        </div>
      ))}
      {inputJSONMode ? (
        <TestRunJsonInput values={values} setValues={setValues} />
      ) : (
        <TestRunForm values={values} setValues={setValues} />
      )}
      {/* 运行中（流式）时，直接在表单区域下方展示实时输出，而不是覆盖整块内容 */}
      {streamMode && isRunning && (
        <>
          <NodeStatusGroup title={I18n.t('Logs') + ' (Live)'} data={streamLogs} optional disableCollapse />
          <NodeStatusGroup title={I18n.t('Context') + ' (Live)'} data={streamCtx} optional disableCollapse />
        </>
      )}

      {/* 展示后端返回的执行信息：仅在非流式或流式已结束时显示，避免与实时输出重复 */}
      {(!streamMode || !isRunning) && (
        <>
          <NodeStatusGroup title={I18n.t('Logs')} data={result?.logs} optional disableCollapse />
          <NodeStatusGroup title={I18n.t('Context')} data={result?.ctx} optional disableCollapse />
        </>
      )}
    </div>
  );

  const renderButton = (
    <Button
      onClick={isRunning ? (streamMode ? onCancelRun : undefined) : onTestRun}
      disabled={isRunning && !streamMode}
      icon={
        // 仅用按钮转圈提示运行中
        isRunning ? <IconSpin spin size="small" /> : <IconPlay size="small" />
      }
      className={classnames(styles.button, {
        [styles.running]: isRunning,
        [styles.default]: !isRunning,
      })}
    >
      {isRunning ? I18n.t('Running...') : I18n.t('Test Run')}
    </Button>
  );

  return (
    <SideSheet
      title={I18n.t('Test Run')}
      visible={visible}
      mask={false}
      motion={false}
      onCancel={onClose}
      width={500}
      headerStyle={{
        display: 'none',
      }}
      bodyStyle={{
        padding: 0,
      }}
      style={{
        background: 'none',
        boxShadow: 'none',
      }}
    >
      <div className={styles['testrun-panel-container']}>
        <div className={styles['testrun-panel-header']}>
          <div className={styles['testrun-panel-title']}>{I18n.t('Test Run')}</div>
          <Button
            className={styles['testrun-panel-title']}
            type="tertiary"
            icon={<IconClose />}
            size="small"
            theme="borderless"
            onClick={onClose}
          />
        </div>
        <div className={styles['testrun-panel-content']}>
          {/* 始终展示表单与结果区域；运行中不再使用覆盖层 */}
          {renderForm}
        </div>
        <div className={styles['testrun-panel-footer']}>{renderButton}</div>
      </div>
    </SideSheet>
  );
};
