import React, { useMemo } from "react";
import classnames from "classnames";
import { ViewedWord, selectionState, composingDataState } from "../../model";
import { Controller, PID } from "@tingwu/core";
import { useRecoilValue } from "recoil";
import { ViewWordsStyled } from "./styled";
import { getElements } from "./calcCharacter";

export interface ViewWordProps {
  controller: Controller;
  word: ViewedWord;
  offset: number;
  composingOffset: number | undefined;
  composingData: string | undefined;
  isHighlight: boolean;
}

export function ViewWord(props: ViewWordProps) {
  const { controller, word, offset, composingOffset, composingData, isHighlight } = props;

  const cls = useMemo(() => {
    return classnames({
      strong: word.isAgendaHighlight,
      played: word.isPlayed,
      base: !word.isPlayed,
      qna: word.qnaModel === "question" ? !word.isQnaActive && word.isQna : undefined, // TODO QA data2
      actived: word.qnaModel === "question" ? word.isQnaActive : undefined,
      "word-blue": word.tag === "blue",
      "word-yellow": word.tag === "yellow",
      "word-red": word.tag === "red",
    });
  }, [word]);

  const content = useMemo(() => {
    let elements;
    try {
      elements = getElements(controller, word, isHighlight, offset, composingOffset, composingData);
    } catch (err) {
      console.error("getElements 执行失败", err);
      return null;
    }

    // 👇 关键：处理非法情况
    if (elements == null) {
      return null;
    }

    if (!Array.isArray(elements)) {
      // 如果不是数组，直接返回（比如已经是单个 React 元素）
      return React.isValidElement(elements) ? elements : null;
    }

    // 如果是数组 → 必须确保每一项都合法，并且有 key
    return elements.map((child, index) => {
      // 过滤掉 undefined / null
      if (child == null) {
        // 可选：打日志定位哪个位置有问题
        console.warn(`[ViewWord] getElements 返回了 null/undefined 元素`, { word, index });
        return <React.Fragment key={`empty-${index}`} />;
      }

      if (typeof child === "string" || typeof child === "number") {
        return <React.Fragment key={`text-${index}`}>{child}</React.Fragment>;
      }

      if (React.isValidElement(child)) {
        // 确保有 key
        return child.key != null ? child : React.cloneElement(child, { key: index });
      }
      return <React.Fragment key={`invalid-${index}`} />;
    });
  }, [controller, word, offset, composingOffset, composingData, isHighlight]);

  return (
    <span data-word-id={word.wid} className={cls}>
      {content}
    </span>
  );
}

export interface ViewedWordsProps {
  controller: Controller;
  pid: PID;
  words: ViewedWord[];
  isHighlight: boolean;
}

export function ViewedWords(props: ViewedWordsProps) {
  const { controller, pid, words, isHighlight } = props;
  const selection = useRecoilValue(selectionState);
  const composingData = useRecoilValue(composingDataState);

  const composingDataOffset = useMemo(() => {
    if (!selection || !selection.isCaret()) {
      return null;
    }
    const { pid: _pid, offset } = selection.range.start;

    if (_pid !== pid) {
      return null;
    }
    return offset;
  }, [selection, pid]);

  let offset = 0;
  return (
    <ViewWordsStyled className={"view-words"} isHighlight={isHighlight}>
      {words.map(word => {
        const { length } = word.text;
        const nextOffset = offset + length;
        let composingOffset: number | undefined;

        if (
          composingData &&
          composingDataOffset != null &&
          ((composingDataOffset > offset && offset) || !offset) &&
          composingDataOffset <= nextOffset
        ) {
          composingOffset = composingDataOffset - offset;
        }
        const isComposing = composingData != null && composingOffset != null;

        const viewWord = (
          <ViewWord
            offset={offset}
            controller={controller}
            key={word.wid}
            word={word}
            composingOffset={isComposing ? composingOffset : undefined}
            composingData={isComposing ? composingData : undefined}
            isHighlight={isHighlight}
          />
        );

        offset = nextOffset;
        return viewWord;
      })}
    </ViewWordsStyled>
  );
}
