import {
  CheckCircle2,
  ChevronDown,
  HelpCircle,
  ListChecks,
  ListTree,
  LockKeyhole,
  ShieldCheck,
  XCircle,
  type LucideIcon
} from "lucide-react";
import { AnimatePresence, motion } from "motion/react";
import { useId, useState, type ReactNode } from "react";
import { cn } from "@/shared/ui/cn";
import { Button } from "@/shared/ui/button";
import {
  StatusBadge,
  StatusDot,
  StatusIcon as SemanticStatusIcon,
  type StatusActivity,
  type StatusTone
} from "@/shared/ui/status";
import {
  agentEase,
  agentLayoutTransition,
  agentPanelItemVariants,
  agentPanelListVariants,
  agentPanelSectionVariants
} from "./agent-motion";
import type {
  AgentChatMessage,
  AgentChatProgress,
  AgentPermissionReply,
  AgentPermissionRequest,
  AgentQuestionInfo,
  AgentQuestionRequest,
  AgentTodoUpdate
} from "../types";

type ProgressStatus = AgentChatProgress["status"];
type TodoStatus = AgentTodoUpdate["todos"][number]["status"];
type PermissionStatus = AgentPermissionRequest["status"];
type QuestionStatus = AgentQuestionRequest["status"];

const STREAMING_PROGRESS_LIMIT = 3;

type AgentStatusMeta = {
  label: string;
  tone: StatusTone;
  activity?: StatusActivity;
};

const progressStatusMeta: Record<ProgressStatus, AgentStatusMeta> = {
  running: {
    label: "进行中",
    tone: "info",
    activity: "live"
  },
  completed: {
    label: "已完成",
    tone: "success"
  },
  error: {
    label: "失败",
    tone: "danger"
  }
};

const todoStatusMeta: Record<TodoStatus, AgentStatusMeta> = {
  completed: {
    label: "完成",
    tone: "success"
  },
  in_progress: {
    label: "进行中",
    tone: "info",
    activity: "live"
  },
  pending: {
    label: "待办",
    tone: "neutral"
  },
  cancelled: {
    label: "取消",
    tone: "neutral"
  }
};

const permissionStatusMeta: Record<PermissionStatus, AgentStatusMeta> = {
  approved_always: { label: "已始终允许", tone: "success" },
  approved_once: { label: "已允许一次", tone: "success" },
  rejected: { label: "已拒绝", tone: "danger" },
  aborted: { label: "已中止", tone: "neutral" },
  error: { label: "失败", tone: "danger" },
  submitting: { label: "提交中", tone: "info", activity: "live" },
  pending: { label: "待确认", tone: "warning" }
};

const questionStatusMeta: Record<QuestionStatus, AgentStatusMeta> = {
  answered: { label: "已回答", tone: "success" },
  rejected: { label: "已跳过", tone: "neutral" },
  error: { label: "失败", tone: "danger" },
  submitting: { label: "提交中", tone: "info", activity: "live" },
  pending: { label: "待回答", tone: "info" }
};

export function AgentMessageDetails({
  message,
  onReplyPermission,
  onReplyQuestion,
  onRejectQuestion
}: {
  message: AgentChatMessage;
  onReplyPermission?: (permission: AgentPermissionRequest, reply: AgentPermissionReply) => Promise<void> | void;
  onReplyQuestion?: (request: AgentQuestionRequest, answers: string[][]) => Promise<void> | void;
  onRejectQuestion?: (request: AgentQuestionRequest) => Promise<void> | void;
}) {
  const hasDetails = Boolean(
    message.todos?.todos.length ||
      message.permissions?.length ||
      message.questions?.length
  );

  if (!hasDetails) {
    return null;
  }

  return (
    <motion.div className="space-y-2" variants={agentPanelListVariants} animate="animate">
      <AnimatePresence initial={false} mode="popLayout">
        {message.todos ? (
          <motion.div key="todos" initial="initial" animate="animate" exit="exit" variants={agentPanelItemVariants}>
            <TodoCard todoUpdate={message.todos} />
          </motion.div>
        ) : null}
        {message.permissions?.length ? (
          <motion.div key="permissions" initial="initial" animate="animate" exit="exit" variants={agentPanelItemVariants}>
            <PermissionList permissions={message.permissions} onReplyPermission={onReplyPermission} />
          </motion.div>
        ) : null}
        {message.questions?.length ? (
          <motion.div key="questions" initial="initial" animate="animate" exit="exit" variants={agentPanelItemVariants}>
            <QuestionList
              questions={message.questions}
              onReplyQuestion={onReplyQuestion}
              onRejectQuestion={onRejectQuestion}
            />
          </motion.div>
        ) : null}
      </AnimatePresence>
    </motion.div>
  );
}

export function AgentMessageProgress({
  progress,
  running
}: {
  progress?: AgentChatProgress[];
  running: boolean;
}) {
  if (!progress?.length) {
    return null;
  }

  return <ProgressList progress={progress} running={running} />;
}

function AgentNestedBlock({
  icon: Icon,
  iconClassName,
  title,
  children,
  floating = false
}: {
  icon: LucideIcon;
  iconClassName: string;
  title: string;
  children: ReactNode;
  floating?: boolean;
}) {
  return (
    <div className={cn(floating ? "px-0 py-1" : "agent-panel-nested rounded-xl p-2.5")}>
      <div className="mb-2 flex items-center gap-2 text-xs font-semibold text-slate-600">
        <Icon size={14} className={iconClassName} aria-hidden="true" />
        {title}
      </div>
      {children}
    </div>
  );
}

function AnimatedDetailItem({ children }: { children: ReactNode }) {
  return (
    <motion.div
      initial="initial"
      animate="animate"
      exit="exit"
      variants={agentPanelItemVariants}
      transition={agentLayoutTransition}
    >
      {children}
    </motion.div>
  );
}

function ProgressList({ progress, running }: { progress: AgentChatProgress[]; running: boolean }) {
  const [expanded, setExpanded] = useState(false);
  const listId = useId();
  const compact = running && !expanded;
  const visibleProgress = expanded
    ? progress
    : running
      ? progress.slice(-STREAMING_PROGRESS_LIMIT)
      : [];
  const listMode = expanded ? "expanded" : running ? "streaming" : "collapsed";

  return (
    <div className="agent-panel-nested rounded-xl p-2.5" data-agent-progress>
      <div className="flex min-h-8 items-center gap-2 px-0.5">
        <ListTree size={15} className="shrink-0 text-blue-600" aria-hidden="true" />
        <span className="min-w-0 flex-1 truncate text-xs font-semibold text-slate-700">执行进度</span>
        <span className="flex shrink-0 items-center gap-1.5 text-xs font-medium text-slate-500">
          {running ? (
            <StatusDot tone="info" activity="live" aria-hidden="true" />
          ) : null}
          <span>{running ? (expanded ? "全部" : "最近") : "步骤"}</span>
          <span className="font-mono tabular-nums text-slate-600">
            {expanded || !running
              ? progress.length
              : Math.min(progress.length, STREAMING_PROGRESS_LIMIT)}
            <span className="px-0.5 text-slate-300">/</span>
            {progress.length}
          </span>
        </span>
        <button
          type="button"
          className={cn(
            "-my-1 -mr-1.5 grid h-10 w-10 shrink-0 place-items-center rounded-md text-slate-400 transition-colors [@media(hover:hover)]:hover:text-blue-600",
            expanded && "text-blue-500"
          )}
          aria-controls={listId}
          aria-expanded={expanded}
          aria-label={expanded ? "收起执行进度" : "展开执行进度"}
          data-state={expanded ? "open" : "closed"}
          onClick={() => setExpanded((current) => !current)}
        >
          <ChevronDown
            size={14}
            className={cn("transition-transform duration-150", expanded && "rotate-180")}
            aria-hidden="true"
          />
        </button>
      </div>
      <AnimatePresence initial={false} mode="wait">
        {visibleProgress.length ? (
          <motion.ol
            key={listMode}
            id={listId}
            aria-label={expanded ? "全部执行进度" : "最近执行进度"}
            className="mt-2 flex flex-col gap-1.5 overflow-hidden"
            initial={{ height: 0, opacity: 0 }}
            animate={{ height: "auto", opacity: 1 }}
            exit={{ height: 0, opacity: 0 }}
            transition={agentLayoutTransition}
          >
            <AnimatePresence initial={false} mode="popLayout">
              {visibleProgress.map((item) => (
                <motion.li
                  key={item.id}
                  className={cn(
                    "grid grid-cols-[16px_minmax(0,1fr)_auto] items-start gap-2 text-xs",
                    compact && "h-12 overflow-hidden"
                  )}
                  initial={{ opacity: 0, y: 4 }}
                  animate={{ opacity: 1, y: 0 }}
                  exit={{ opacity: 0, y: -2 }}
                  transition={{ duration: 0.16, ease: agentEase }}
                >
                  <StatusDot
                    tone={progressStatusMeta[item.status].tone}
                    activity={progressStatusMeta[item.status].activity}
                    size="md"
                    className="mt-1"
                  />
                  <span className="min-w-0">
                    <span
                      className={cn(
                        "block font-semibold text-slate-800",
                        compact ? "truncate" : "break-words"
                      )}
                      title={item.title}
                    >
                      {item.title}
                    </span>
                    {item.detail ? (
                      <span
                        className={cn(
                          "block leading-5 text-slate-500",
                          compact ? "truncate" : "break-words"
                        )}
                        title={item.detail}
                      >
                        {item.detail}
                      </span>
                    ) : null}
                  </span>
                  <AnimatePresence initial={false} mode="wait">
                    <motion.span
                      key={item.status}
                      data-progress-status={item.status}
                      className={cn(
                        "min-w-12 pt-0.5 text-right text-xs font-semibold",
                        item.status === "running"
                          ? "text-blue-600"
                          : [
                              `status-tone-${progressStatusMeta[item.status].tone}`,
                              "text-[var(--status-foreground)]"
                            ]
                      )}
                      initial={{ opacity: 0 }}
                      animate={{ opacity: 1 }}
                      exit={{ opacity: 0 }}
                      transition={{ duration: 0.14 }}
                    >
                      {progressStatusMeta[item.status].label}
                    </motion.span>
                  </AnimatePresence>
                </motion.li>
              ))}
            </AnimatePresence>
          </motion.ol>
        ) : null}
      </AnimatePresence>
    </div>
  );
}

function TodoCard({ todoUpdate }: { todoUpdate: AgentTodoUpdate }) {
  return (
    <AgentNestedBlock icon={ListChecks} iconClassName="status-tone-success text-[var(--status-foreground)]" title="计划">
      <motion.ol className="space-y-1.5" variants={agentPanelListVariants} animate="animate">
        <AnimatePresence initial={false} mode="popLayout">
          {todoUpdate.todos.slice(0, 6).map((todo) => (
            <motion.li
              key={todo.id}
              className="grid grid-cols-[16px_1fr_auto] items-start gap-2 text-xs"
              initial="initial"
              animate="animate"
              exit="exit"
              variants={agentPanelItemVariants}
              transition={agentLayoutTransition}
            >
              <SemanticStatusIcon
                tone={todoStatusMeta[todo.status].tone}
                activity={todoStatusMeta[todo.status].activity}
                shape="circle"
                size="xs"
                className="mt-0.5"
              >
                {todo.status === "completed" ? <CheckCircle2 size={10} /> : null}
              </SemanticStatusIcon>
              <span className="min-w-0 truncate text-slate-700" title={todo.content}>
                {todo.content}
              </span>
              <StatusBadge
                tone={todoStatusMeta[todo.status].tone}
                activity={todoStatusMeta[todo.status].activity}
              >
                {todoStatusMeta[todo.status].label}
              </StatusBadge>
            </motion.li>
          ))}
        </AnimatePresence>
      </motion.ol>
    </AgentNestedBlock>
  );
}

function PermissionList({
  permissions,
  onReplyPermission
}: {
  permissions: AgentPermissionRequest[];
  onReplyPermission?: (permission: AgentPermissionRequest, reply: AgentPermissionReply) => Promise<void> | void;
}) {
  return (
    <AgentNestedBlock icon={LockKeyhole} iconClassName="status-tone-warning text-[var(--status-foreground)]" title="权限请求">
      <motion.div className="flex flex-col gap-1.5" variants={agentPanelListVariants} animate="animate">
        <AnimatePresence initial={false} mode="popLayout">
          {permissions.slice(-4).map((permission) => (
            <AnimatedDetailItem key={permission.requestId}>
              <PermissionRequestCard
                permission={permission}
                onReplyPermission={onReplyPermission}
              />
            </AnimatedDetailItem>
          ))}
        </AnimatePresence>
      </motion.div>
    </AgentNestedBlock>
  );
}

function PermissionRequestCard({
  permission,
  onReplyPermission
}: {
  permission: AgentPermissionRequest;
  onReplyPermission?: (permission: AgentPermissionRequest, reply: AgentPermissionReply) => Promise<void> | void;
}) {
  const actionable = permission.status === "pending" || permission.status === "error";
  const submitting = permission.status === "submitting";
  const canApproveAlways = permission.always.length > 0;
  const disabled = !onReplyPermission || submitting;
  const target = permission.target ?? (permission.patterns.join(" ") || permission.permission);

  const reply = (nextReply: AgentPermissionReply) => {
    if (!onReplyPermission || submitting) {
      return;
    }
    void Promise.resolve(onReplyPermission(permission, nextReply));
  };

  return (
    <div className="surface-reading rounded-lg px-2.5 py-2 text-xs">
      <div className="grid grid-cols-[1fr_auto] gap-2">
        <div className="min-w-0">
          <p className="truncate font-semibold text-slate-800" title={permission.target ?? permission.permission}>
            {getPermissionTitle(permission)}
          </p>
          <p className="mt-0.5 truncate font-mono text-xs text-slate-500" title={target}>
            {target}
          </p>
          {permission.error ? (
            <p className="status-tone-danger mt-1 line-clamp-2 text-xs leading-4 text-[var(--status-foreground)]">{permission.error}</p>
          ) : null}
        </div>
        <StatusBadge
          tone={permissionStatusMeta[permission.status].tone}
          activity={permissionStatusMeta[permission.status].activity}
          className="self-start"
        >
          {permissionStatusMeta[permission.status].label}
        </StatusBadge>
      </div>
      <AnimatePresence initial={false}>
        {actionable ? (
          <motion.div
            key="permission-actions"
            className="mt-2 flex flex-wrap items-center gap-2"
            initial="initial"
            animate="animate"
            exit="exit"
            variants={agentPanelSectionVariants}
            style={{ overflow: "hidden" }}
          >
            <Button
              type="button"
              size="sm"
              className="h-8 px-2 text-xs"
              disabled={disabled}
              onClick={() => reply("once")}
            >
              <CheckCircle2 size={13} aria-hidden="true" />
              允许一次
            </Button>
            {canApproveAlways ? (
              <Button
                type="button"
                size="sm"
                variant="outline"
                className="h-8 border-blue-200 bg-blue-50 px-2 text-xs text-blue-700 hover:bg-blue-100"
                disabled={disabled}
                onClick={() => reply("always")}
              >
                <ShieldCheck size={13} aria-hidden="true" />
                始终允许
              </Button>
            ) : null}
            <Button
              type="button"
              size="sm"
              variant="outline"
              className="status-tone-danger h-8 border-[var(--status-border)] bg-[var(--status-soft)] px-2 text-xs text-[var(--status-foreground)] [@media(hover:hover)]:hover:brightness-95"
              disabled={disabled}
              onClick={() => reply("reject")}
            >
              <XCircle size={13} aria-hidden="true" />
              拒绝
            </Button>
          </motion.div>
        ) : null}
      </AnimatePresence>
    </div>
  );
}

function QuestionList({
  questions,
  onReplyQuestion,
  onRejectQuestion
}: {
  questions: AgentQuestionRequest[];
  onReplyQuestion?: (request: AgentQuestionRequest, answers: string[][]) => Promise<void> | void;
  onRejectQuestion?: (request: AgentQuestionRequest) => Promise<void> | void;
}) {
  return (
    <AgentNestedBlock icon={HelpCircle} iconClassName="text-blue-600" title="补充信息" floating>
      <motion.div className="flex flex-col gap-1.5" variants={agentPanelListVariants} animate="animate">
        <AnimatePresence initial={false} mode="popLayout">
          {questions.slice(-3).map((request) => (
            <AnimatedDetailItem key={request.requestId}>
              <QuestionRequestCard
                request={request}
                onReplyQuestion={onReplyQuestion}
                onRejectQuestion={onRejectQuestion}
              />
            </AnimatedDetailItem>
          ))}
        </AnimatePresence>
      </motion.div>
    </AgentNestedBlock>
  );
}

function QuestionRequestCard({
  request,
  onReplyQuestion,
  onRejectQuestion
}: {
  request: AgentQuestionRequest;
  onReplyQuestion?: (request: AgentQuestionRequest, answers: string[][]) => Promise<void> | void;
  onRejectQuestion?: (request: AgentQuestionRequest) => Promise<void> | void;
}) {
  const [draftAnswers, setDraftAnswers] = useState(() => createInitialQuestionAnswers(request));
  const actionable = request.status === "pending" || request.status === "error";
  const submitting = request.status === "submitting";
  const disabled = submitting || !onReplyQuestion;
  const answers = normalizeDraftAnswers(draftAnswers);
  const canSubmit = actionable && answers.length === request.questions.length && answers.every((answer) => answer.length > 0);

  const submit = () => {
    if (!onReplyQuestion || !canSubmit) {
      return;
    }
    void Promise.resolve(onReplyQuestion(request, answers));
  };

  const reject = () => {
    if (!onRejectQuestion || submitting) {
      return;
    }
    void Promise.resolve(onRejectQuestion(request));
  };

  return (
    <div className="px-0 py-1 text-xs">
      <div className="mb-2 flex items-center justify-between gap-2">
        <span className="min-w-0 truncate font-semibold text-slate-800">
          {request.questions[0]?.header || "问题"}
        </span>
        <StatusBadge
          tone={questionStatusMeta[request.status].tone}
          activity={questionStatusMeta[request.status].activity}
        >
          {questionStatusMeta[request.status].label}
        </StatusBadge>
      </div>

      <div className="space-y-3">
        {request.questions.map((question, questionIndex) => (
          <QuestionInput
            key={`${request.requestId}-${questionIndex}`}
            question={question}
            questionIndex={questionIndex}
            value={draftAnswers[questionIndex] ?? { selected: [], customSelected: false, custom: "" }}
            disabled={!actionable || submitting}
            onChange={(nextValue) =>
              setDraftAnswers((current) => current.map((item, index) => (index === questionIndex ? nextValue : item)))
            }
          />
        ))}
      </div>

      {request.error ? (
        <p className="status-tone-danger mt-2 line-clamp-2 text-xs leading-4 text-[var(--status-foreground)]">{request.error}</p>
      ) : null}
      {request.answers?.length ? (
        <p className="mt-2 truncate text-slate-500" title={request.answers.map((answer) => answer.join("、")).join("；")}>
          已答：{request.answers.map((answer) => answer.join("、")).join("；")}
        </p>
      ) : null}

      <AnimatePresence initial={false}>
        {actionable ? (
          <motion.div
            key="question-actions"
            className="mt-2 flex flex-wrap items-center gap-2"
            initial="initial"
            animate="animate"
            exit="exit"
            variants={agentPanelSectionVariants}
            style={{ overflow: "hidden" }}
          >
            <Button
              type="button"
              size="sm"
              className="h-8 px-2 text-xs"
              disabled={disabled || !canSubmit}
              onClick={submit}
            >
              <CheckCircle2 size={13} aria-hidden="true" />
              提交回答
            </Button>
            <Button
              type="button"
              size="sm"
              variant="outline"
              className="h-8 border-slate-200 bg-white px-2 text-xs text-slate-600 hover:bg-slate-50"
              disabled={submitting || !onRejectQuestion}
              onClick={reject}
            >
              <XCircle size={13} aria-hidden="true" />
              跳过
            </Button>
          </motion.div>
        ) : null}
      </AnimatePresence>
    </div>
  );
}

type QuestionDraftAnswer = {
  selected: string[];
  customSelected: boolean;
  custom: string;
};

function QuestionInput({
  question,
  questionIndex,
  value,
  disabled,
  onChange
}: {
  question: AgentQuestionInfo;
  questionIndex: number;
  value: QuestionDraftAnswer;
  disabled: boolean;
  onChange: (value: QuestionDraftAnswer) => void;
}) {
  const inputId = useId();
  const name = `agent-question-${questionIndex}-${inputId}`;
  const customEnabled = question.custom !== false;
  const showCustomChoice = customEnabled && question.options.length > 0;
  const showCustomInput = customEnabled && value.customSelected;

  const toggleOption = (label: string, checked: boolean) => {
    if (question.multiple) {
      onChange({
        ...value,
        selected: checked ? [...value.selected, label] : value.selected.filter((item) => item !== label)
      });
      return;
    }
    onChange({
      ...value,
      selected: checked ? [label] : [],
      customSelected: false
    });
  };

  const toggleCustom = (checked: boolean) => {
    onChange({
      ...value,
      selected: question.multiple ? value.selected : [],
      customSelected: checked
    });
  };

  return (
    <div className="space-y-1.5">
      <p className="leading-5 text-slate-700">{question.question}</p>
      {question.options.length || showCustomChoice ? (
        <div className="space-y-1">
          {question.options.map((option) => {
            const checked = value.selected.includes(option.label);
            return (
              <label
                key={option.label}
                className={cn(
                  "flex cursor-pointer items-start gap-2 rounded-md border px-2 py-1.5 transition-colors",
                  checked
                    ? "border-blue-300 bg-blue-50/70 text-blue-700"
                    : "border-slate-200/80 text-slate-600 hover:border-slate-300 hover:bg-slate-50/70",
                  disabled && "cursor-default opacity-60"
                )}
              >
                <input
                  type={question.multiple ? "checkbox" : "radio"}
                  name={name}
                  className="mt-0.5 h-3.5 w-3.5 accent-blue-600"
                  checked={checked}
                  disabled={disabled}
                  onChange={(event) => toggleOption(option.label, event.currentTarget.checked)}
                />
                <span className="min-w-0">
                  <span className="block font-semibold text-slate-700">{option.label}</span>
                  {option.description ? (
                    <span className="block leading-4 text-slate-500">{option.description}</span>
                  ) : null}
                </span>
              </label>
            );
          })}
          {showCustomChoice ? (
            <label
              className={cn(
                "flex cursor-pointer items-center gap-2 rounded-md border px-2 py-2 transition-colors",
                value.customSelected
                  ? "border-blue-300 bg-blue-50/70 text-blue-700"
                  : "border-slate-200/80 text-slate-600 hover:border-slate-300 hover:bg-slate-50/70",
                disabled && "cursor-default opacity-60"
              )}
            >
              <input
                type={question.multiple ? "checkbox" : "radio"}
                name={name}
                className="h-3.5 w-3.5 accent-blue-600"
                checked={value.customSelected}
                disabled={disabled}
                onChange={(event) => toggleCustom(event.currentTarget.checked)}
              />
              <span className="font-semibold text-slate-700">自定义回答</span>
            </label>
          ) : null}
        </div>
      ) : null}
      {showCustomInput ? (
        <textarea
          className="min-h-16 w-full resize-none rounded-md border border-slate-200/80 bg-transparent px-2 py-1.5 text-xs leading-5 text-slate-700 outline-hidden transition focus:border-blue-300 focus:ring-2 focus:ring-blue-100 disabled:opacity-60"
          aria-label="自定义回答内容"
          placeholder="输入自定义回答"
          value={value.custom}
          disabled={disabled}
          onChange={(event) => onChange({ ...value, custom: event.currentTarget.value })}
        />
      ) : null}
    </div>
  );
}

function createInitialQuestionAnswers(request: AgentQuestionRequest): QuestionDraftAnswer[] {
  return request.questions.map((question, index) => {
    const optionLabels = new Set(question.options.map((option) => option.label));
    const savedAnswers = request.answers?.[index] ?? [];
    const selected = savedAnswers.filter((answer) => optionLabels.has(answer));
    const customAnswers = savedAnswers.filter((answer) => !optionLabels.has(answer));
    const customEnabled = question.custom !== false;

    return {
      selected,
      customSelected: customEnabled && (question.options.length === 0 || customAnswers.length > 0),
      custom: customAnswers.join("；")
    };
  });
}

function normalizeDraftAnswers(draftAnswers: QuestionDraftAnswer[]) {
  return draftAnswers.map((answer) => {
    const custom = answer.custom.trim();
    return answer.customSelected && custom ? [...answer.selected, custom] : answer.selected;
  });
}

function getPermissionTitle(permission: AgentPermissionRequest) {
  if (permission.permission === "bash") return "执行终端命令";
  if (permission.permission === "edit") return "修改文件内容";
  if (permission.permission === "external_directory") return "访问工作区外目录";
  return permission.permission || "工具权限请求";
}
