/**
 * TextArea — 多行文本输入（Input 的多行变体）
 *
 * **何时用**：备注、描述、反馈内容等需换行与较长文本录入的场景。
 * **不用**：单行短文本 → `Input`；合法值来自枚举 → `Select` / `Radio`。
 *
 * **规格**：仅 MD 一种尺寸（与 Input `size="md"` 的字号与内边距一致），不再提供 `size` 配置。
 *
 * @prop {'default'|'code'} [variant='default'] — 输入框类型；code 用于代码 / Prompt 输入，左侧显示行号，使用等宽字体
 * @prop {1|2|3|4|5} [minRows=3] — 默认占位行数（最小高度）；1 行时外框 36px 与 Input md 对齐（上下各 8px + 20px 行高），仍可按内容增高
 * @prop {'default'|'error'} [status='default'] — 状态
 * @prop {boolean} [disabled=false] — 是否禁用
 * @prop {number} [maxLength] — 可选字数上限；不传则不限制字数。与 showCount 搭配可展示「当前/上限」或仅「当前」
 * @prop {boolean} [showCount=false] — 是否展示字数；无 maxLength 时仅展示已输入字数
 * @prop {boolean} [enforceMaxLength=true] — 有 maxLength 时是否用原生属性阻止继续输入（false 时仅标红计数）
 * @prop {string} [aiSuggestion] — 单条 AI 推荐草稿
 * @prop {string[]} [aiSuggestions] — 多条 AI 推荐草稿
 * @prop {string} [placeholder='请输入'] — 占位
 * @prop {string} value — 当前值（受控）
 * @prop {string} [defaultValue] — 非受控初值
 * @prop {(e: import('react').ChangeEvent<HTMLTextAreaElement>) => void} [onChange] — 变更回调
 * @prop {(e: import('react').UIEvent<HTMLTextAreaElement>) => void} [onScroll] — 滚动回调
 * @prop {(suggestion: string) => void} [onAdoptSuggestion] — 采纳 AI 推荐回调
 * @prop {() => void} [onRefreshAiSuggestions] — 刷新 AI 推荐回调
 * @prop {boolean} [aiSuggestionsLoading=false] — AI 推荐区加载中
 * @prop {number} [aiSuggestionsLoadingCount=1] — 加载骨架推荐条数（1–5）
 * @prop {'vertical'|'none'} [resize='vertical'] — 高度模式：`vertical` 随内容增高；`none` 固定为默认行高、超出内滚
 * @prop {boolean} [fillHeight=false] — 是否填满父容器高度；用于卡片主编辑区，需配合父容器 `flex-1 min-h-0`
 * @prop {React.CSSProperties} [style] — 内联样式
 * @prop {string} [className=''] — 类名
 */
import { useState, useCallback, useRef, useLayoutEffect, useEffect, useMemo } from 'react';
import AiSuggestionPanel, {
  AiRefreshButton,
  buildSuggestionGroupsFromFlatList,
} from './AiSuggestionShared';
import { AiSuggestionPanelSkeleton } from './Skeleton';

/* ── 尺寸与最小高度（行高 20px；多行竖向 padding 12px；单行外框 36px 对齐 Input md） ── */
const LINE_HEIGHT_PX = 20;
const PADDING_Y_PX = 12;
const SINGLE_ROW_MIN_PX = 36;
const CODE_VERTICAL_PADDING_PX = 32;

function clampMinRows(raw) {
  const n = Number(raw);
  if (!Number.isFinite(n)) return 3;
  return Math.min(5, Math.max(1, Math.round(n)));
}

function getTextAreaMinContentHeightPx(minRows) {
  const rows = clampMinRows(minRows);
  if (rows === 1) return SINGLE_ROW_MIN_PX;
  return PADDING_Y_PX + rows * LINE_HEIGHT_PX;
}

function getTextAreaVerticalPaddingClass(minRows) {
  return clampMinRows(minRows) === 1 ? 'py-2' : 'py-[6px]';
}

/* ── 根容器 ── */
const WRAPPER_BASE = [
  'inline-flex flex-col items-start w-[var(--size-input-width,100%)] max-w-full gap-1',
  '[font-family:inherit]',
].join(' ');

const WRAPPER_FILL_HEIGHT = 'flex-1 min-h-0 w-full';

/* ── 外层字段框（边框 + 圆角） ── */
const FIELD_BASE = [
  'group relative flex w-full min-w-0 flex-col',
  'border [border-style:solid] rounded-md',
  'transition-colors tfds-motion-ui-fast',
  'outline-none',
  '[font-family:inherit]',
].join(' ');

const FIELD_FILL_HEIGHT = 'flex-1 min-h-0';

/* ── 状态 → 容器背景 / 边框（聚焦仅 1px 描边，聚焦优先级高于悬浮） ── */
const STATUS_CLASS = {
  default: [
    'bg-surface border-border-default',
    'hover:border-border-strong',
    'focus-within:border-primary',
    'focus-within:hover:border-primary',
  ].join(' '),
  error: [
    'bg-red-50 border-border-default',
    'hover:bg-red-100',
    'focus-within:border-red-500',
    'focus-within:hover:border-red-500 focus-within:hover:bg-red-50',
  ].join(' '),
};

/* ── 禁用态（覆盖 status 颜色，保留边框） ── */
const DISABLED_CLASS = 'bg-disabled border-border-default cursor-not-allowed opacity-60';

/* ── 原生 textarea 正文区（透明无边框；正文固定走 14px / 20px token） ── */
const TEXTAREA_BODY_BASE = [
  'w-full box-border resize-none bg-transparent border-none outline-none',
  'px-3 [font-size:var(--text-sm)]',
  'text-foreground placeholder:text-foreground-muted',
  'focus:placeholder:text-foreground-disabled',
  '[font-family:inherit] [line-height:var(--leading-5)]',
  'disabled:text-foreground-disabled disabled:placeholder:text-foreground-disabled disabled:cursor-not-allowed',
  'm-0',
].join(' ');

const TEXTAREA_BODY_FILL_HEIGHT = 'flex-1 min-h-0 h-full';

/* ── resize 模式 → 滚动 / 自动增高 ── */
const TEXTAREA_OVERFLOW_CLASS = {
  autogrow: 'overflow-hidden',
  fixed: 'overflow-y-auto',
};

/* ── 字数统计行 ── */
const COUNT_ROW = 'flex w-full shrink-0 items-center justify-end px-3 pb-[6px] pt-0.5';

const COUNT_TEXT_CLASS = 'text-xs leading-4 tabular-nums text-foreground-muted';
const COUNT_TEXT_DISABLED = 'text-xs leading-4 tabular-nums text-foreground-disabled';
const COUNT_OVERFLOW_CLASS = 'text-xs leading-4 tabular-nums text-red-500';
const AI_REFRESH_FLOATING_WRAPPER_CLASS = [
  'absolute top-2 right-2 z-[1] inline-flex',
  'opacity-0 group-hover:opacity-100 group-focus-within:opacity-100',
  'transition-opacity tfds-motion-ui-fast',
].join(' ');

const AI_REFRESH_BUTTON_CLASS = [
  'inline-flex items-center justify-center shrink-0',
  'h-6 w-6 rounded-md',
  'bg-surface border border-border-default',
  'cursor-pointer p-0',
  'hover:border-border-strong',
  'transition-colors tfds-motion-ui-fast',
].join(' ');

const TEXTAREA_LOCAL_FALLBACK_SUGGESTION_GROUPS = [
  ['您好，这边已根据当前对话内容整理出一版备注草稿，您可以继续补充后保存。'],
  ['根据当前上下文，建议先记录用户诉求、异常现象以及下一步跟进动作。'],
];

function dispatchNativeTextareaValue(textarea, nextValue) {
  if (!textarea) return;
  const descriptor = Object.getOwnPropertyDescriptor(window.HTMLTextAreaElement.prototype, 'value');
  descriptor?.set?.call(textarea, nextValue);
  textarea.dispatchEvent(new Event('input', { bubbles: true }));
}

export default function TextArea({
  variant = 'default',
  minRows = 3,
  status = 'default',
  disabled = false,
  maxLength,
  showCount = false,
  enforceMaxLength = true,
  aiSuggestion,
  aiSuggestions,
  placeholder = '请输入',
  value,
  defaultValue,
  onChange,
  onScroll,
  onAdoptSuggestion,
  onRefreshAiSuggestions,
  aiSuggestionsLoading = false,
  aiSuggestionsLoadingCount = 1,
  resize = 'vertical',
  fillHeight = false,
  style,
  className = '',
  ...rest
}) {
  const isControlled = value !== undefined;
  const [innerValue, setInnerValue] = useState(defaultValue ?? '');
  const textareaRef = useRef(null);
  const gutterInnerRef = useRef(null);
  const [isSuggestionDismissed, setIsSuggestionDismissed] = useState(false);
  const [suggestionIndex, setSuggestionIndex] = useState(0);
  const autogrow = resize === 'vertical';
  const minRowsClamped = clampMinRows(minRows);
  const resolvedVariant = variant === 'code' ? 'code' : 'default';
  const isCodeVariant = resolvedVariant === 'code';

  const currentValue = isControlled ? (value ?? '') : innerValue;
  const length = typeof currentValue === 'string' ? currentValue.length : 0;
  const hasMax = typeof maxLength === 'number' && maxLength >= 0;
  const showCounter = Boolean(showCount);
  const isOverLimit = hasMax && length > maxLength;
  const nativeMaxLength = hasMax && enforceMaxLength ? maxLength : undefined;
  const shouldFillHeight = Boolean(fillHeight);
  const suggestionGroups = useMemo(() => {
    const source = Array.isArray(aiSuggestions) && aiSuggestions.length > 0
      ? aiSuggestions
      : (typeof aiSuggestion === 'string' && aiSuggestion.trim().length > 0 ? [aiSuggestion] : []);
    return buildSuggestionGroupsFromFlatList(source, TEXTAREA_LOCAL_FALLBACK_SUGGESTION_GROUPS);
  }, [aiSuggestion, aiSuggestions]);
  const activeSuggestions = suggestionGroups[suggestionIndex] || suggestionGroups[0] || [];
  const suggestionList = useMemo(
    () => activeSuggestions.map((item, index) => ({
      id: `textarea-ai-suggestion-${suggestionIndex}-${index}`,
      label: item,
      value: item,
    })),
    [activeSuggestions, suggestionIndex]
  );
  const canRefreshLocally = suggestionGroups.length > 1;
  const showSuggestion = !disabled && !isSuggestionDismissed && suggestionList.length > 0;
  const showAiRefresh = !disabled && suggestionList.length > 0
    && (canRefreshLocally || typeof onRefreshAiSuggestions === 'function');

  const minContentPx = isCodeVariant
    ? CODE_VERTICAL_PADDING_PX + minRowsClamped * LINE_HEIGHT_PX
    : getTextAreaMinContentHeightPx(minRowsClamped);

  const handleChange = useCallback((e) => {
    if (!isControlled) setInnerValue(e.target.value);
    onChange?.(e);
  }, [isControlled, onChange]);

  const handleScroll = useCallback((e) => {
    if (gutterInnerRef.current) {
      gutterInnerRef.current.style.transform = `translateY(-${e.currentTarget.scrollTop}px)`;
    }
    onScroll?.(e);
  }, [onScroll]);

  useEffect(() => {
    setIsSuggestionDismissed(false);
    setSuggestionIndex(0);
  }, [aiSuggestion, aiSuggestions]);

  const handleAdoptSuggestion = useCallback((suggestion) => {
    if (!suggestion?.value) return;
    dispatchNativeTextareaValue(textareaRef.current, suggestion.value);
    onAdoptSuggestion?.(suggestion.value);
    setIsSuggestionDismissed(true);
    textareaRef.current?.focus();
  }, [onAdoptSuggestion]);

  const handleRefreshAiSuggestions = useCallback(() => {
    if (canRefreshLocally) {
      setSuggestionIndex((prev) => (prev + 1) % suggestionGroups.length);
    }
    onRefreshAiSuggestions?.();
    setIsSuggestionDismissed(false);
    textareaRef.current?.focus();
  }, [canRefreshLocally, onRefreshAiSuggestions, suggestionGroups.length]);

  useLayoutEffect(() => {
    const el = textareaRef.current;
    if (!el) return;
    if (shouldFillHeight) {
      el.style.minHeight = '0px';
      el.style.height = '100%';
      el.style.maxHeight = 'none';
      return;
    }
    if (!autogrow) {
      el.style.minHeight = `${minContentPx}px`;
      el.style.height = `${minContentPx}px`;
      el.style.maxHeight = `${minContentPx}px`;
      return;
    }
    el.style.minHeight = `${minContentPx}px`;
    el.style.maxHeight = 'none';
    const emptySingle =
      minRowsClamped === 1 && String(currentValue).length === 0;
    if (emptySingle) {
      el.style.height = `${minContentPx}px`;
      return;
    }
    el.style.height = 'auto';
    el.style.height = `${Math.max(el.scrollHeight, minContentPx)}px`;
  }, [autogrow, minContentPx, currentValue, showCounter, hasMax, minRowsClamped, shouldFillHeight]);

  const wrapperCls = [
    WRAPPER_BASE,
    shouldFillHeight ? WRAPPER_FILL_HEIGHT : '',
    className,
  ].filter(Boolean).join(' ');
  const fieldCls = [
    FIELD_BASE,
    shouldFillHeight ? FIELD_FILL_HEIGHT : '',
    disabled ? DISABLED_CLASS : STATUS_CLASS[status],
    !disabled && isCodeVariant && status === 'default' ? '!bg-blueGrey-100' : '',
  ].filter(Boolean).join(' ');

  const bodyCls = [
    TEXTAREA_BODY_BASE,
    isCodeVariant ? '' : getTextAreaVerticalPaddingClass(minRowsClamped),
    isCodeVariant ? 'px-4 py-4 font-mono' : '',
    shouldFillHeight ? TEXTAREA_BODY_FILL_HEIGHT : '',
    TEXTAREA_OVERFLOW_CLASS[shouldFillHeight ? 'fixed' : (autogrow ? 'autogrow' : 'fixed')],
  ].join(' ');

  const codeLines = String(currentValue || '').split('\n');
  const lineCount = Math.max(minRowsClamped, codeLines.length || 1);
  const lineNumbers = Array.from({ length: lineCount }, (_, index) => index + 1);
  const codeRowCls = [
    'flex w-full min-w-0',
    shouldFillHeight ? 'flex-1 min-h-0' : '',
  ].filter(Boolean).join(' ');
  const codeGutterCls = [
    'relative shrink-0 overflow-hidden bg-transparent px-3 py-4 text-right font-mono [font-size:var(--text-sm)] [line-height:var(--leading-5)]',
    'tabular-nums text-blueGrey-400 select-none',
  ].join(' ');
  const textareaStyle = style;

  const countCls = disabled ? COUNT_TEXT_DISABLED : (isOverLimit ? COUNT_OVERFLOW_CLASS : COUNT_TEXT_CLASS);

  return (
    <div className={[`tfds-textarea`, wrapperCls].filter(Boolean).join(' ')} data-tfds-component="TextArea">
      <div className={fieldCls}>
        {showAiRefresh ? (
          <div className={AI_REFRESH_FLOATING_WRAPPER_CLASS}>
            <AiRefreshButton
              onClick={handleRefreshAiSuggestions}
              className={AI_REFRESH_BUTTON_CLASS}
            />
          </div>
        ) : null}
        {isCodeVariant ? (
          <div className={codeRowCls}>
            <div className={codeGutterCls} aria-hidden="true">
              <div ref={gutterInnerRef}>
                {lineNumbers.map((line) => (
                  <div key={line}>{line}</div>
                ))}
              </div>
            </div>
            <textarea
              ref={textareaRef}
              className={bodyCls}
              placeholder={placeholder}
              disabled={disabled}
              value={isControlled ? value : undefined}
              defaultValue={isControlled ? undefined : defaultValue}
              onChange={handleChange}
              onScroll={handleScroll}
              maxLength={nativeMaxLength}
              spellCheck={false}
              {...rest}
              style={textareaStyle}
            />
          </div>
        ) : (
          <textarea
            ref={textareaRef}
            className={bodyCls}
            placeholder={placeholder}
            disabled={disabled}
            value={isControlled ? value : undefined}
            defaultValue={isControlled ? undefined : defaultValue}
            onChange={handleChange}
            onScroll={handleScroll}
            maxLength={nativeMaxLength}
            {...rest}
            style={textareaStyle}
          />
        )}
        {showCounter ? (
          <div className={COUNT_ROW} aria-live="polite">
            <span className={countCls}>
              {length}
              {hasMax ? `/${maxLength}` : null}
            </span>
          </div>
        ) : null}
      </div>
      {aiSuggestionsLoading ? (
        <AiSuggestionPanelSkeleton count={aiSuggestionsLoadingCount} />
      ) : null}
      {!aiSuggestionsLoading && showSuggestion ? (
        <AiSuggestionPanel
          suggestions={suggestionList}
          onSelect={handleAdoptSuggestion}
          getSuggestionKey={(suggestion) => suggestion.id}
          getSuggestionLabel={(suggestion) => suggestion.label}
          getSuggestionAriaLabel={(_, index) => `采纳 AI 推荐草稿 ${index + 1}`}
        />
      ) : null}
    </div>
  );
}
