// Component Terminal - Giao diện điều khiển thiết bị từ xa qua MQTT
// Không hỗ trợ thiết bị loại GMSv5

import { getBadgeConnection } from '@/components/shared/ThingShared';
import { ROUTE_MANAGER_DEVICES } from '@/constants/routes';
import { apiGetThingDetail } from '@/services/master/ThingController';
import { mqttClient } from '@/utils/mqttClient';
import { BgColorsOutlined, ClearOutlined } from '@ant-design/icons';
import { PageContainer, ProCard } from '@ant-design/pro-components';
import { history, useIntl, useModel, useParams } from '@umijs/max';
import {
  Alert,
  Button,
  Dropdown,
  MenuProps,
  Result,
  Space,
  Spin,
  Typography,
  theme,
} from 'antd';
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { v4 as uuidv4 } from 'uuid';
import XTerm, { XTermHandle } from './components/XTerm';

// Format SenML record nhận từ MQTT

const TERMINAL_THEMES: MasterModel.TerminalThemes = {
  dark: {
    name: 'Dark',
    theme: {
      background: '#141414',
      foreground: '#ffffff',
      cursor: '#ffffff',
      selectionBackground: 'rgba(255, 255, 255, 0.3)',
    },
  },
  light: {
    name: 'Light',
    theme: {
      background: '#ffffff',
      foreground: '#000000',
      cursor: '#000000',
      selectionBackground: 'rgba(0, 0, 0, 0.3)',
    },
  },
  green: {
    name: 'Green (Classic)',
    theme: {
      background: '#000000',
      foreground: '#00ff00',
      cursor: '#00ff00',
      selectionBackground: 'rgba(0, 255, 0, 0.3)',
    },
  },
  amber: {
    name: 'Amber',
    theme: {
      background: '#1a0f00',
      foreground: '#ffaa00',
      cursor: '#ffaa00',
      selectionBackground: 'rgba(255, 170, 0, 0.3)',
    },
  },
  solarized: {
    name: 'Solarized Dark',
    theme: {
      background: '#002b36',
      foreground: '#839496',
      cursor: '#93a1a1',
      selectionBackground: 'rgba(7, 54, 66, 0.99)',
    },
  },
};

/**
 * Encode chuỗi sang Base64 để gửi qua MQTT
 * Hỗ trợ Unicode characters qua UTF-8 normalization
 * Áp dụng cho tất cả thiết bị (ngoại trừ GMSv5)
 */
const encodeBase64 = (value: string) => {
  try {
    const normalized = encodeURIComponent(value).replace(
      /%([0-9A-F]{2})/g,
      (_, hex: string) => String.fromCharCode(Number.parseInt(hex, 16)),
    );
    if (typeof globalThis !== 'undefined' && globalThis.btoa) {
      return globalThis.btoa(normalized);
    }
  } catch (error) {
    console.error('Failed to encode terminal payload', error);
  }
  return value;
};

/**
 * Parse payload nhận từ MQTT (format SenML)
 * Trích xuất các field 'vs' và join thành string hoàn chỉnh
 */
const parseTerminalPayload = (payload: string): string => {
  try {
    const records: MasterModel.SenmlRecord[] = JSON.parse(payload);
    if (!Array.isArray(records)) {
      return '';
    }
    return records
      .map((record) => record?.vs ?? '')
      .filter(Boolean)
      .join('');
  } catch (error) {
    console.error('Failed to parse terminal payload', error);
    return '';
  }
};

const DeviceTerminalPage = () => {
  // Lấy thingId từ URL params
  const { thingId } = useParams<{ thingId: string }>();
  const { initialState } = useModel('@@initialState');
  const { token } = theme.useToken();
  const intl = useIntl();

  // States quản lý trạng thái thiết bị và terminal
  const [thing, setThing] = useState<MasterModel.Thing | null>(null); // Thông tin thiết bị
  const [isLoading, setIsLoading] = useState<boolean>(false); // Đang load dữ liệu
  const [terminalError, setTerminalError] = useState<string | null>(null); // Lỗi terminal
  const [isSessionReady, setIsSessionReady] = useState<boolean>(false); // Session đã sẵn sàng
  const [connectionState, setConnectionState] =
    useState<MasterModel.TerminalConnection>({
      online: false,
    }); // Trạng thái online/offline
  const [sessionAttempt, setSessionAttempt] = useState<number>(0); // Số lần thử kết nối lại
  const [selectedThemeKey, setSelectedThemeKey] = useState<string>('dark'); // Theme được chọn

  // Refs lưu trữ các giá trị không trigger re-render
  const sessionIdRef = useRef(uuidv4()); // ID phiên terminal duy nhất
  const terminalRef = useRef<XTermHandle | null>(null); // Reference đến XTerm component
  const responseTopicRef = useRef<string | null>(null); // Topic MQTT nhận phản hồi
  const requestTopicRef = useRef<string | null>(null); // Topic MQTT gửi yêu cầu
  const handshakeCompletedRef = useRef<boolean>(false); // Đã hoàn thành handshake chưa
  const mqttCleanupRef = useRef<Array<() => void>>([]); // Mảng cleanup functions

  const credentialMetadata = initialState?.currentUserProfile?.metadata;

  /**
   * Lấy MQTT credentials từ metadata user
   * Username: frontend_thing_id
   * Password: frontend_thing_key
   */
  const mqttCredentials = useMemo(() => {
    const username = credentialMetadata?.frontend_thing_id;
    const password = credentialMetadata?.frontend_thing_key;
    if (username && password) {
      return {
        username,
        password,
      };
    }
    return null;
  }, [credentialMetadata]);

  // Kiểm tra thiết bị có hỗ trợ terminal không (không hỗ trợ GMSv5)
  const supportsTerminal = thing?.metadata?.type !== 'gmsv5';
  // Channel ID để gửi lệnh điều khiển
  const ctrlChannelId = thing?.metadata?.ctrl_channel_id;

  /**
   * Dọn dẹp MQTT connection khi unmount hoặc reconnect
   - Hủy subscribe topic
   - Reset các ref
   */
  const tearDownMqtt = useCallback(() => {
    mqttCleanupRef.current.forEach((dispose) => {
      try {
        dispose();
      } catch {
        // ignore individual cleanup errors
      }
    });
    mqttCleanupRef.current = [];
    if (responseTopicRef.current) {
      mqttClient.unsubscribe(responseTopicRef.current);
      responseTopicRef.current = null;
    }
    requestTopicRef.current = null;
    handshakeCompletedRef.current = false;
  }, []);

  /**
   * Fetch thông tin chi tiết thiết bị từ API
   - Cập nhật state thing
   - Cập nhật trạng thái online/offline
   - Xóa lỗi nếu fetch thành công
   */
  const fetchThingDetail = useCallback(async () => {
    if (!thingId) {
      return;
    }
    setIsLoading(true);
    try {
      const resp = await apiGetThingDetail(thingId);
      setThing(resp);
      const isConnected = Boolean(resp?.metadata?.connected);
      setConnectionState({
        online: isConnected,
        lastSeen: isConnected ? undefined : Date.now(),
      });
      setTerminalError(null);
    } catch (error) {
      console.error('Failed to load device details', error);
      setTerminalError(
        intl.formatMessage({
          id: 'terminal.loadDeviceError',
          defaultMessage: 'Không thể tải thông tin thiết bị.',
        }),
      );
    } finally {
      setIsLoading(false);
    }
  }, [intl, thingId]);

  // Fetch thông tin thiết bị khi component mount
  useEffect(() => {
    fetchThingDetail();
  }, [fetchThingDetail]);

  // Tạo sessionId mới khi thingId thay đổi
  useEffect(() => {
    sessionIdRef.current = uuidv4();
  }, [thingId]);

  // Khôi phục theme từ localStorage khi component mount
  useEffect(() => {
    const savedTheme = localStorage.getItem('terminal_theme_key');
    if (savedTheme && TERMINAL_THEMES[savedTheme]) {
      setSelectedThemeKey(savedTheme);
    }
  }, []);

  /**
   * Gửi lệnh tới thiết bị qua MQTT
   * Format SenML: bn(base name), n(name), bt(base time), vs(value string)
   */
  const publishTerminalCommand = useCallback((command: string) => {
    const topic = requestTopicRef.current;
    if (!topic || !command) {
      return;
    }
    const payload: MasterModel.MqttTerminalPayload = [
      {
        bn: `${sessionIdRef.current}:`,
        n: 'term',
        bt: Math.floor(Date.now() / 1000),
        vs: encodeBase64(command),
      },
    ];
    mqttClient.publish(topic, JSON.stringify(payload));
  }, []);

  /**
   * Setup MQTT connection và handlers
   - Tạo topics để giao tiếp với thiết bị
   - Đăng ký handlers cho các sự kiện MQTT
   - Thực hiện handshake khi kết nối thành công
   */
  useEffect(() => {
    if (!supportsTerminal || !mqttCredentials || !ctrlChannelId) {
      return;
    }

    tearDownMqtt();
    // Tạo topics: response topic duy nhất cho mỗi session, request topic chung
    const responseTopic = `channels/${ctrlChannelId}/messages/res/term/${sessionIdRef.current}`;
    const requestTopic = `channels/${ctrlChannelId}/messages/req`;
    responseTopicRef.current = responseTopic;
    requestTopicRef.current = requestTopic;
    handshakeCompletedRef.current = false;

    /**
     * Handler khi MQTT kết nối thành công
     - Subscribe response topic
     - Gửi lệnh handshake: 'open' và 'cd /'
     */
    const handleConnect = () => {
      setTerminalError(null);
      if (!handshakeCompletedRef.current) {
        mqttClient.subscribe(responseTopic);
        publishTerminalCommand('open');
        publishTerminalCommand('cd /');
        handshakeCompletedRef.current = true;
      }
      setIsSessionReady(true);
    };

    /**
     * Handler nhận message từ thiết bị
     - Parse payload SenML
     - Ghi dữ liệu ra terminal
     */
    const handleMessage = (topic: string, message: Buffer) => {
      if (topic !== responseTopic) {
        return;
      }
      const text = parseTerminalPayload(message.toString());
      if (text) {
        terminalRef.current?.write(text);
      }
    };

    /**
     * Handler khi MQTT bị ngắt kết nối
     */
    const handleClose = () => {
      setIsSessionReady(false);
    };

    /**
     * Handler khi có lỗi MQTT
     - Dọn dẹp connection
     - Hiển thị thông báo lỗi
     */
    const handleError = (error: Error) => {
      console.error('MQTT terminal error', error);
      setIsSessionReady(false);
      tearDownMqtt();
      mqttClient.disconnect();
      setTerminalError(
        intl.formatMessage({
          id: 'terminal.mqttError',
          defaultMessage: 'Không thể kết nối MQTT.',
        }),
      );
    };

    // Kết nối MQTT và đăng ký handlers
    mqttClient.connect(mqttCredentials);
    const offConnect = mqttClient.onConnect(handleConnect);
    const offMessage = mqttClient.onMessage(handleMessage);
    const offClose = mqttClient.onClose(handleClose);
    const offError = mqttClient.onError(handleError);
    mqttCleanupRef.current = [offConnect, offMessage, offClose, offError];

    // Nếu đã kết nối thì xử lý luôn
    if (mqttClient.isConnected()) {
      handleConnect();
    }

    // Cleanup khi unmount hoặc dependencies thay đổi
    return () => {
      tearDownMqtt();
      mqttClient.disconnect();
      setIsSessionReady(false);
    };
  }, [
    ctrlChannelId,
    intl,
    mqttCredentials,
    publishTerminalCommand,
    sessionAttempt,
    supportsTerminal,
    tearDownMqtt,
  ]);

  /**
   * Xử lý khi người dùng nhập liệu vào terminal
   - Gửi từng ký tự với prefix 'c,'
   - Chỉ gửi khi session đã sẵn sàng
   */
  const handleTyping = useCallback(
    (data: string) => {
      if (!isSessionReady) {
        return;
      }
      publishTerminalCommand(`c,${data}`);
    },
    [isSessionReady, publishTerminalCommand],
  );

  /**
   * Xử lý retry khi có lỗi
   - Tạo sessionId mới
   - Tăng sessionAttempt để trigger reconnect
   - Fetch lại thông tin thiết bị
   */
  const handleRetry = () => {
    setTerminalError(null);
    setSessionAttempt((prev) => prev + 1);
    sessionIdRef.current = uuidv4();
    fetchThingDetail();
  };

  // Label hiển thị trạng thái kết nối
  const connectionLabel = connectionState.online
    ? intl.formatMessage({ id: 'common.online', defaultMessage: 'Online' })
    : intl.formatMessage({ id: 'common.offline', defaultMessage: 'Offline' });

  /**
   * Render kết quả blocking (lỗi, warning, info)
   - Hiển thị nút quay lại và thử lại
   */
  const renderBlockingResult = (
    status: 'info' | 'warning' | 'error' | '404',
    title: string,
    subTitle?: string,
  ) => (
    <Result
      status={status}
      title={title}
      subTitle={subTitle}
      extra={[
        <Button key="back" onClick={() => history.push(ROUTE_MANAGER_DEVICES)}>
          {intl.formatMessage({
            id: 'common.back',
            defaultMessage: 'Quay lại',
          })}
        </Button>,
        <Button key="retry" type="primary" onClick={handleRetry}>
          {intl.formatMessage({
            id: 'common.retry',
            defaultMessage: 'Thử lại',
          })}
        </Button>,
      ]}
    />
  );

  // Tiêu đề trang
  const pageTitle =
    thing?.name ||
    intl.formatMessage({
      id: 'terminal.pageTitle',
      defaultMessage: 'Terminal',
    });

  /**
   * Điều kiện hiển thị terminal
   - Tất cả điều kiện phải true
   */
  const showTerminal =
    !isLoading &&
    !terminalError &&
    thing &&
    supportsTerminal &&
    ctrlChannelId &&
    mqttCredentials;

  /**
   * Theme của terminal theo dark/light mode
   */
  const terminalTheme = useMemo(() => {
    return (
      TERMINAL_THEMES[selectedThemeKey]?.theme || TERMINAL_THEMES.dark.theme
    );
  }, [selectedThemeKey]);

  /**
   * Xử lý thay đổi theme
   * - Cập nhật state
   * - Lưu vào localStorage
   */
  const handleThemeChange: MenuProps['onClick'] = (e) => {
    const themeKey = e.key;
    setSelectedThemeKey(themeKey);
    localStorage.setItem('terminal_theme_key', themeKey);
  };

  /**
   * Menu items cho theme selector
   */
  const themeMenuItems: MenuProps['items'] = Object.entries(
    TERMINAL_THEMES,
  ).map(([key, preset]) => ({
    key,
    label: preset.name,
  }));

  // ===== RENDER =====
  return (
    <PageContainer
      header={{
        title: pageTitle,
        onBack: () => history.push(ROUTE_MANAGER_DEVICES),
        tags: (
          <Space size={8}>
            {getBadgeConnection(connectionState.online)}
            <Typography.Text>{connectionLabel}</Typography.Text>
          </Space>
        ),
      }}
    >
      {/* Loading state */}
      {isLoading && (
        <div
          style={{
            minHeight: 320,
            display: 'flex',
            alignItems: 'center',
            justifyContent: 'center',
          }}
        >
          <Spin size="large" />
        </div>
      )}

      {/* Generic error state */}
      {!isLoading && terminalError && (
        <Result
          status="error"
          title={intl.formatMessage({
            id: 'terminal.genericError',
            defaultMessage: 'Đã có lỗi xảy ra',
          })}
          subTitle={terminalError}
          extra={[
            <Button key="retry" type="primary" onClick={handleRetry}>
              {intl.formatMessage({
                id: 'common.retry',
                defaultMessage: 'Thử lại',
              })}
            </Button>,
          ]}
        />
      )}

      {/* Thiết bị không hỗ trợ terminal (không phải GMSv6) */}
      {!isLoading &&
        !terminalError &&
        thing &&
        !supportsTerminal &&
        renderBlockingResult(
          'info',
          intl.formatMessage({
            id: 'terminal.unsupported.title',
            defaultMessage: 'Thiết bị không hỗ trợ terminal',
          }),
          intl.formatMessage({
            id: 'terminal.unsupported.desc',
            defaultMessage:
              'Thiết bị GMSv5 không được hỗ trợ. Vui lòng sử dụng thiết bị khác.',
          }),
        )}

      {/* Thiếu ctrl_channel_id */}
      {!isLoading &&
        !terminalError &&
        thing &&
        supportsTerminal &&
        !ctrlChannelId &&
        renderBlockingResult(
          'warning',
          intl.formatMessage({
            id: 'terminal.missingChannel.title',
            defaultMessage: 'Thiếu thông tin kênh điều khiển',
          }),
          intl.formatMessage({
            id: 'terminal.missingChannel.desc',
            defaultMessage:
              'Thiết bị chưa được cấu hình ctrl_channel_id nên không thể mở terminal.',
          }),
        )}

      {/* Thiếu MQTT credentials */}
      {!isLoading &&
        !terminalError &&
        thing &&
        supportsTerminal &&
        ctrlChannelId &&
        !mqttCredentials &&
        renderBlockingResult(
          'warning',
          intl.formatMessage({
            id: 'terminal.missingCredential.title',
            defaultMessage: 'Thiếu thông tin xác thực',
          }),
          intl.formatMessage({
            id: 'terminal.missingCredential.desc',
            defaultMessage:
              'Tài khoản hiện tại chưa được cấp frontend_thing_id/frontend_thing_key.',
          }),
        )}

      {/* Terminal UI - chỉ hiển thị khi tất cả điều kiện thỏa mãn */}
      {showTerminal && (
        <Space direction="vertical" style={{ width: '100%' }} size="large">
          {/* Cảnh báo khi thiết bị offline */}
          {!connectionState.online && (
            <Alert
              type="warning"
              showIcon
              message={intl.formatMessage({
                id: 'terminal.offline',
                defaultMessage:
                  'Thiết bị đang ngoại tuyến. Terminal chuyển sang chế độ chỉ xem.',
              })}
            />
          )}
          {/* Đang kết nối MQTT */}
          {connectionState.online && !isSessionReady && (
            <Alert
              type="info"
              showIcon
              message={intl.formatMessage({
                id: 'terminal.connecting',
                defaultMessage: 'Đang chuẩn bị phiên terminal...',
              })}
            />
          )}
          {/* XTerm component */}
          <ProCard
            bordered
            bodyStyle={{ padding: 0, height: 560 }}
            style={{
              minHeight: 560,
              border: `2px solid ${token.colorBorder}`,
              backgroundColor: '#000',
            }}
          >
            <XTerm
              ref={terminalRef}
              cursorBlink
              disabled={!connectionState.online || !isSessionReady}
              onData={handleTyping}
              theme={terminalTheme}
            />
          </ProCard>
          {/* Nút xóa màn hình */}
          <Space>
            <Button
              icon={<ClearOutlined />}
              onClick={() => terminalRef.current?.clear()}
              disabled={!isSessionReady}
            >
              {intl.formatMessage({
                id: 'terminal.action.clear',
                defaultMessage: 'Xóa màn hình',
              })}
            </Button>
            <Dropdown
              menu={{
                items: themeMenuItems,
                onClick: handleThemeChange,
                selectedKeys: [selectedThemeKey],
              }}
              placement="topLeft"
            >
              <Button icon={<BgColorsOutlined />}>
                {intl.formatMessage({
                  id: 'terminal.action.theme',
                  defaultMessage: 'Theme',
                })}
                : {TERMINAL_THEMES[selectedThemeKey]?.name || 'Dark'}
              </Button>
            </Dropdown>
          </Space>
        </Space>
      )}
    </PageContainer>
  );
};

export default DeviceTerminalPage;
