import {
  CheckCircle2,
  ChevronDown,
  HelpCircle,
  ListChecks,
  ListTree,
  LockKeyhole,
  ShieldCheck,
  XCircle,
  type LucideIcon
} from "lucide-react";
import { AnimatePresence, motion } from "motion/react";
import { useState, type ReactNode } from "react";
import { cn } from "@/shared/ui/cn";
import { Button } from "@/shared/ui/button";
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;

const progressStatusMeta: Record<ProgressStatus, { label: string; className: string; dotClassName: string }> = {
  running: {
    label: "进行中",
    className: "bg-blue-100 text-blue-700",
    dotClassName: "border-blue-500 bg-blue-100"
  },
  completed: {
    label: "完成",
    className: "bg-emerald-100 text-emerald-700",
    dotClassName: "border-emerald-500 bg-emerald-100"
  },
  error: {
    label: "失败",
    className: "bg-red-100 text-red-700",
    dotClassName: "border-red-500 bg-red-100"
  }
};

const todoStatusMeta: Record<TodoStatus, { label: string; className: string; dotClassName: string }> = {
  completed: {
    label: "完成",
    className: "bg-emerald-100 text-emerald-700",
    dotClassName: "border-emerald-500 bg-emerald-500"
  },
  in_progress: {
    label: "进行中",
    className: "bg-blue-100 text-blue-700",
    dotClassName: "border-blue-500 bg-blue-100"
  },
  pending: {
    label: "待办",
    className: "bg-slate-100 text-slate-600",
    dotClassName: "border-slate-300 bg-white"
  },
  cancelled: {
    label: "取消",
    className: "bg-slate-200 text-slate-500",
    dotClassName: "border-slate-300 bg-slate-200"
  }
};

const permissionStatusMeta: Record<PermissionStatus, { label: string; className: string }> = {
  approved_always: { label: "已始终允许", className: "bg-emerald-100 text-emerald-700" },
  approved_once: { label: "已允许一次", className: "bg-emerald-100 text-emerald-700" },
  rejected: { label: "已拒绝", className: "bg-red-100 text-red-700" },
  aborted: { label: "已中止", className: "bg-slate-200 text-slate-500" },
  error: { label: "失败", className: "bg-red-100 text-red-700" },
  submitting: { label: "提交中", className: "bg-blue-100 text-blue-700" },
  pending: { label: "待确认", className: "bg-orange-100 text-orange-700" }
};

const questionStatusMeta: Record<QuestionStatus, { label: string; className: string }> = {
  answered: { label: "已回答", className: "bg-emerald-100 text-emerald-700" },
  rejected: { label: "已跳过", className: "bg-slate-200 text-slate-500" },
  error: { label: "失败", className: "bg-red-100 text-red-700" },
  submitting: { label: "提交中", className: "bg-blue-100 text-blue-700" },
  pending: { label: "待回答", className: "bg-blue-100 text-blue-700" }
};

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 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">
      <button
        type="button"
        className="flex w-full items-center gap-2 text-left text-xs font-semibold text-slate-600"
        aria-expanded={expanded}
        onClick={() => setExpanded((current) => !current)}
      >
        <ListTree size={16} className="shrink-0 text-blue-700" aria-hidden="true" />
        <span>执行进度</span>
        <span className="flex-1" />
        <span className="shrink-0 font-normal text-slate-400">
          {expanded
            ? `全部 ${progress.length} 条`
            : running
              ? `最近 ${Math.min(progress.length, STREAMING_PROGRESS_LIMIT)} 条`
              : `共 ${progress.length} 条`}
        </span>
        <ChevronDown
          size={14}
          className={cn("shrink-0 text-slate-400 transition-transform", expanded && "rotate-180")}
          aria-hidden="true"
        />
      </button>
      <AnimatePresence initial={false} mode="wait">
        {visibleProgress.length ? (
          <motion.ol
            key={listMode}
            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 }}
                >
                  <motion.span
                    aria-hidden="true"
                    className={cn(
                      "mt-0.5 h-3.5 w-3.5 rounded-full border transition-colors duration-150",
                      progressStatusMeta[item.status].dotClassName
                    )}
                    animate={
                      item.status === "running"
                        ? { opacity: [0.45, 1, 0.45], scale: 1 }
                        : item.status === "error"
                          ? { opacity: [0.55, 1, 1], scale: [1, 1.28, 1] }
                          : { opacity: 1, scale: 1 }
                    }
                    transition={
                      item.status === "running"
                        ? { duration: 1.2, ease: "easeInOut", repeat: Infinity }
                        : item.status === "error"
                          ? { duration: 0.28, ease: agentEase }
                        : { duration: 0.14 }
                    }
                  />
                  <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 rounded-full px-2 py-0.5 text-center font-semibold",
                        progressStatusMeta[item.status].className
                      )}
                      initial={{ opacity: 0 }}
                      animate={
                        item.status === "error"
                          ? { opacity: [0, 1, 1], x: [0, -2, 2, -1, 0] }
                          : { opacity: 1, x: 0 }
                      }
                      exit={{ opacity: 0 }}
                      transition={
                        item.status === "error"
                          ? { duration: 0.24, ease: agentEase }
                          : { 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="text-emerald-600" 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}
            >
              <span
                className={cn(
                  "mt-0.5 grid h-3.5 w-3.5 place-items-center rounded-full border",
                  todoStatusMeta[todo.status].dotClassName
                )}
              >
                {todo.status === "completed" ? <CheckCircle2 size={10} className="text-white" aria-hidden="true" /> : null}
              </span>
              <span className="min-w-0 truncate text-slate-700" title={todo.content}>
                {todo.content}
              </span>
              <span className={cn("rounded-full px-2 py-0.5 font-semibold", todoStatusMeta[todo.status].className)}>
                {todoStatusMeta[todo.status].label}
              </span>
            </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="text-orange-600" 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="mt-1 line-clamp-2 text-xs leading-4 text-red-600">{permission.error}</p>
          ) : null}
        </div>
        <span className={cn("self-start rounded-full px-2 py-0.5 font-semibold", permissionStatusMeta[permission.status].className)}>
          {permissionStatusMeta[permission.status].label}
        </span>
      </div>
      <AnimatePresence initial={false}>
        {actionable ? (
          <motion.div
            key="permission-actions"
            className="mt-2 flex flex-wrap items-center gap-1.5"
            initial="initial"
            animate="animate"
            exit="exit"
            variants={agentPanelSectionVariants}
            style={{ overflow: "hidden" }}
          >
            <Button
              type="button"
              size="sm"
              className="agent-panel-primary-action h-7 rounded-md 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-7 rounded-md 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="h-7 rounded-md border-red-200 bg-red-50 px-2 text-xs text-red-700 hover:bg-red-100"
              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>
        <span className={cn("rounded-full px-2 py-0.5 font-semibold", questionStatusMeta[request.status].className)}>
          {questionStatusMeta[request.status].label}
        </span>
      </div>

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

      {request.error ? (
        <p className="mt-2 line-clamp-2 text-xs leading-4 text-red-600">{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-1.5"
            initial="initial"
            animate="animate"
            exit="exit"
            variants={agentPanelSectionVariants}
            style={{ overflow: "hidden" }}
          >
            <Button
              type="button"
              size="sm"
              className="agent-panel-primary-action h-7 rounded-md 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-7 rounded-md 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[];
  custom: string;
};

function QuestionInput({
  question,
  questionIndex,
  value,
  disabled,
  onChange
}: {
  question: AgentQuestionInfo;
  questionIndex: number;
  value: QuestionDraftAnswer;
  disabled: boolean;
  onChange: (value: QuestionDraftAnswer) => void;
}) {
  const name = `agent-question-${questionIndex}-${question.question}`;
  const showCustomInput = question.custom || question.options.length === 0;

  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] : []
    });
  };

  return (
    <div className="space-y-1.5">
      <p className="leading-5 text-slate-700">{question.question}</p>
      {question.options.length ? (
        <div className="space-y-1">
          {question.options.map((option) => {
            const checked = value.selected.includes(option.label);
            return (
              <label
                key={option.label}
                className="flex cursor-pointer items-start gap-2 rounded-md border border-slate-200/80 px-2 py-1.5 text-slate-600"
              >
                <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>
            );
          })}
        </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"
          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) => ({
    selected: request.answers?.[index] ?? [],
    custom: ""
  }));
}

function normalizeDraftAnswers(draftAnswers: QuestionDraftAnswer[]) {
  return draftAnswers.map((answer) => {
    const custom = answer.custom.trim();
    return 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 || "工具权限请求";
}
