"use client";

import {
  ArrowUpDown,
  ArrowUp,
  ChevronsRight,
  Command,
  CornerDownLeft,
  LoaderCircle,
  Plus,
  Square,
  X,
} from "lucide-react";
import {
  ComposerPrimitive,
  useAui,
  useAuiState,
} from "@assistant-ui/react";
import {
  Fragment,
  useId,
  useMemo,
  useRef,
  useState,
  type KeyboardEvent,
} from "react";
import type { Skill } from "@/lib/types";
import { cn } from "@/lib/utils";
import { SkillTag } from "@/components/skill-tag";

export function ChatComposer({
  skills,
  selectedIds,
  resolvedSkillIds,
  onSelectedChange,
  onOpenSkillPicker,
  isStreaming,
  isStopping,
}: {
  skills: Skill[];
  selectedIds: string[];
  resolvedSkillIds: string[] | null;
  onSelectedChange: (ids: string[]) => void;
  onOpenSkillPicker: () => void;
  isStreaming: boolean;
  isStopping: boolean;
}) {
  const aui = useAui();
  const value = useAuiState((state) => state.composer.text);
  const textareaRef = useRef<HTMLTextAreaElement>(null);
  const listboxId = useId();
  const [activeSuggestionId, setActiveSuggestionId] = useState<string | null>(
    null,
  );

  const slashMatch = value.match(/(?:^|\s)\/([^\s/]*)$/);
  const slashQuery = slashMatch?.[1]?.toLowerCase() ?? null;
  const availableSuggestions = useMemo(() => {
    if (slashQuery === null) return [];
    return skills
      .filter((skill) => !selectedIds.includes(skill.id))
      .filter((skill) =>
        `${skill.name} ${skill.description}`
          .toLowerCase()
          .includes(slashQuery),
      )
      .slice(0, 6);
  }, [skills, selectedIds, slashQuery]);
  const matchedSuggestionIndex = availableSuggestions.findIndex(
    (skill) => skill.id === activeSuggestionId,
  );
  const activeSuggestionIndex =
    matchedSuggestionIndex === -1 ? 0 : matchedSuggestionIndex;

  const selectedSkills = selectedIds
    .map((id) => skills.find((skill) => skill.id === id))
    .filter((skill): skill is Skill => Boolean(skill));
  const routeResolved = isStreaming && resolvedSkillIds !== null;

  function handleKeyDown(event: KeyboardEvent<HTMLTextAreaElement>) {
    if (!slashMatch || event.nativeEvent.isComposing) return;

    if (event.key === "Escape") {
      event.preventDefault();
      event.stopPropagation();
      closeSlashMenu();
      return;
    }

    if (availableSuggestions.length === 0) return;

    if (event.key === "ArrowDown" || event.key === "ArrowUp") {
      event.preventDefault();
      event.stopPropagation();
      const direction = event.key === "ArrowDown" ? 1 : -1;
      const nextIndex =
        (activeSuggestionIndex + direction + availableSuggestions.length) %
        availableSuggestions.length;
      setActiveSuggestionId(availableSuggestions[nextIndex].id);
      return;
    }

    if (
      (event.key === "Enter" && !event.shiftKey) ||
      event.key === "Tab"
    ) {
      event.preventDefault();
      event.stopPropagation();
      addSkill(
        availableSuggestions[
          Math.min(activeSuggestionIndex, availableSuggestions.length - 1)
        ],
      );
    }
  }

  function closeSlashMenu() {
    setActiveSuggestionId(null);
    aui.composer().setText(value.replace(/(?:^|\s)\/([^\s/]*)$/, (match) =>
      match.startsWith(" ") ? " " : "",
    ));
    requestAnimationFrame(() => textareaRef.current?.focus());
  }

  function addSkill(skill: Skill) {
    setActiveSuggestionId(null);
    onSelectedChange([...selectedIds, skill.id]);
    aui.composer().setText(
      value.replace(/(?:^|\s)\/([^\s/]*)$/, (match) =>
        match.startsWith(" ") ? " " : "",
      ),
    );
    requestAnimationFrame(() => textareaRef.current?.focus());
  }

  return (
    <ComposerPrimitive.Root className="relative">
      {slashMatch && (
        <div
          id={listboxId}
          role="listbox"
          aria-label="可添加的 Skill"
          className="absolute bottom-[calc(100%+10px)] left-0 z-20 w-[min(440px,calc(100vw-32px))] overflow-hidden rounded-xl border border-border bg-popover p-1.5 text-popover-foreground shadow-lg"
        >
          <div className="flex items-center justify-between px-2 pb-2 pt-1">
            <span className="text-[11px] font-medium text-muted-foreground">
              插入 Skill
            </span>
            <span className="flex items-center gap-1.5 text-[10px] text-muted-foreground">
              <ArrowUpDown className="size-3" />
              移动
              <span className="text-muted-foreground/45">·</span>
              <CornerDownLeft className="size-3" />
              选择
            </span>
          </div>
          {availableSuggestions.length > 0 ? (
            <div className="space-y-1">
              {availableSuggestions.map((skill, index) => (
                <button
                  key={skill.id}
                  id={`${listboxId}-${skill.id}`}
                  type="button"
                  role="option"
                  aria-selected={index === activeSuggestionIndex}
                  className={cn(
                    "flex w-full items-center gap-3 rounded-lg px-3 py-2.5 text-left hover:bg-accent",
                    index === activeSuggestionIndex && "bg-accent/70",
                  )}
                  onMouseDown={(event) => event.preventDefault()}
                  onMouseEnter={() => setActiveSuggestionId(skill.id)}
                  onClick={() => addSkill(skill)}
                >
                  <span className="grid size-7 place-items-center rounded-md bg-muted font-mono text-[10px] font-bold text-foreground">
                    /
                  </span>
                  <span className="min-w-0">
                    <span className="block truncate text-sm font-medium text-foreground">
                      {skill.name}
                    </span>
                    <span className="mt-0.5 block truncate text-xs text-muted-foreground">
                      {skill.description}
                    </span>
                  </span>
                </button>
              ))}
            </div>
          ) : (
            <div className="rounded-lg bg-muted/50 px-3 py-4 text-center text-xs text-muted-foreground">
              没有更多可添加的 Skill
            </div>
          )}
        </div>
      )}

      <div className="overflow-hidden rounded-3xl border border-border/80 bg-muted/35 shadow-[0_1px_2px_rgba(0,0,0,0.03),0_8px_24px_rgba(0,0,0,0.04)] transition-[border-color,box-shadow] focus-within:border-ring focus-within:shadow-[0_1px_2px_rgba(0,0,0,0.03),0_10px_30px_rgba(0,0,0,0.06)]">
        {selectedSkills.length > 0 && (
          <div className="px-3.5 pt-2.5">
            <div className="flex flex-wrap items-center gap-1">
              {selectedSkills.map((skill, index) => {
                const status = !isStreaming
                  ? "selected"
                  : !routeResolved
                    ? "checking"
                    : resolvedSkillIds.includes(skill.id)
                      ? "used"
                      : "unused";
                return (
                  <Fragment key={skill.id}>
                    {index > 0 && (
                      <ChevronsRight
                        aria-hidden="true"
                        className={cn(
                          "mx-0.5 size-3.5 shrink-0 text-muted-foreground/35",
                          isStreaming &&
                            !routeResolved &&
                            "animate-pulse text-amber-500/70",
                          routeResolved &&
                            resolvedSkillIds?.includes(skill.id) &&
                            "text-emerald-500/80",
                        )}
                      />
                    )}
                    <SkillTag
                      name={skill.name}
                      status={status}
                      showStatusLabel={false}
                      className="h-6 px-2 text-[10px]"
                      onRemove={
                        isStreaming
                          ? undefined
                          : () =>
                              onSelectedChange(
                                selectedIds.filter((id) => id !== skill.id),
                              )
                      }
                    />
                  </Fragment>
                );
              })}
            </div>
          </div>
        )}

        <ComposerPrimitive.Input
          ref={textareaRef}
          onKeyDown={handleKeyDown}
          aria-autocomplete="list"
          aria-controls={slashMatch ? listboxId : undefined}
          aria-expanded={Boolean(slashMatch)}
          aria-activedescendant={
            slashMatch && availableSuggestions.length > 0
              ? `${listboxId}-${
                  availableSuggestions[
                    Math.min(
                      activeSuggestionIndex,
                      availableSuggestions.length - 1,
                    )
                  ].id
                }`
              : undefined
          }
          rows={1}
          maxRows={7}
          placeholder="说说你想解决什么，输入 / 添加 Skill…"
          className="block min-h-[68px] w-full resize-none bg-transparent px-4 pb-1.5 pt-3.5 text-[15px] leading-6 text-foreground outline-none placeholder:text-muted-foreground/70"
          aria-label="聊天输入"
        />

        <div className="flex items-center justify-between px-3 pb-3">
          <button
            type="button"
            onClick={onOpenSkillPicker}
            disabled={isStreaming}
            className="inline-flex h-8 items-center gap-2 rounded-full px-2 text-xs font-medium text-muted-foreground transition-colors hover:bg-accent hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/40 disabled:opacity-55"
          >
            <span className="grid size-6 place-items-center rounded-full border border-border bg-background">
              <Plus className="size-3.5" />
            </span>
            选择 Skill
            {selectedSkills.length > 0 && (
              <span className="grid size-5 place-items-center rounded-full bg-primary text-[10px] text-primary-foreground">
                {selectedSkills.length}
              </span>
            )}
          </button>

          <div className="flex items-center gap-3">
            <span className="hidden items-center gap-1 text-[10px] text-muted-foreground/75 sm:flex">
              <Command className="size-3" />
              Enter 发送
            </span>
            {isStreaming ? (
              <ComposerPrimitive.Cancel
                className="grid size-9 place-items-center rounded-full bg-destructive text-white transition-colors hover:bg-destructive/90 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-destructive/30 focus-visible:ring-offset-2"
                aria-label={
                  isStopping ? "正在中止本轮回答" : "中止本轮回答"
                }
                disabled={isStopping}
              >
                {isStopping ? (
                  <LoaderCircle className="size-4 animate-spin" />
                ) : (
                  <Square className="size-4 fill-current" />
                )}
              </ComposerPrimitive.Cancel>
            ) : (
              <ComposerPrimitive.Send
                className="grid size-9 place-items-center rounded-full bg-primary text-primary-foreground transition-colors hover:bg-primary/90 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/50 focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:bg-muted-foreground/35"
                aria-label="发送消息"
              >
                <ArrowUp className="size-4.5" strokeWidth={2.4} />
              </ComposerPrimitive.Send>
            )}
          </div>
        </div>
      </div>
      <div className="mt-2 flex items-center justify-center gap-1.5 text-[10px] text-muted-foreground/70">
        <span>AI 可能犯错，请核对重要信息</span>
        {slashMatch && (
          <>
            <span>·</span>
            <button
              type="button"
              className="inline-flex items-center gap-1 hover:text-foreground"
              onClick={closeSlashMenu}
            >
              <X className="size-3" /> 关闭 Skill 提示
            </button>
          </>
        )}
      </div>
    </ComposerPrimitive.Root>
  );
}
