'use client';

import Link from 'next/link';
import { useParams, useRouter } from 'next/navigation';
import React, { useMemo, useState } from 'react';
import { AlertCircle, ArrowLeft, Bot, CheckCircle2, Download, FileText, HelpCircle, Loader2, MessageSquare, RefreshCw, ThumbsUp, Trash2, User, XCircle } from 'lucide-react';

import { TaskRuntimeStatusBadge, formatTaskRuntimeDuration, formatTaskRuntimeTime, progressPercent } from '@/components/task-runtime/TaskRuntimeShared';
import { Badge } from '@/components/ui/badge';
import { Button } from '@/components/ui/button';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { Textarea } from '@/components/ui/textarea';
import { deleteBackendTask, getBackendTask, getFileUrl, submitChatFeedback } from '@/lib/api';
import { pickAppText } from '@/lib/i18n/core';
import { useAppI18n } from '@/lib/i18n/provider';
import { buildTaskRuntimeView, type TaskRuntimeNodeView } from '@/lib/task-runtime';
import { useChatStore } from '@/lib/store';
import type { BackendTask, BackendTaskRun, ProcessArtifact, ProcessEvent } from '@/types';

type TaskFeedbackType = 'satisfied' | 'revise' | 'abandon';
type TaskFeedbackItem = {
  feedback_type?: unknown;
  comment?: unknown;
  created_at?: unknown;
  run_id?: unknown;
};

function taskVisibleStatus(task: TaskRuntimeNodeView, locale: 'zh-CN' | 'en-US') {
  if (task.status === 'error') return pickAppText(locale, '任务失败', 'Task failed');
  if (task.status === 'cancelled') return pickAppText(locale, '已取消', 'Cancelled');
  return task.stageLabel || task.status;
}

function downloadText(filename: string, content: string) {
  const url = URL.createObjectURL(new Blob([content], { type: 'text/plain;charset=utf-8' }));
  const anchor = document.createElement('a');
  anchor.href = url;
  anchor.download = filename;
  document.body.appendChild(anchor);
  anchor.click();
  anchor.remove();
  URL.revokeObjectURL(url);
}

export default function TaskDetailPage() {
  const { locale } = useAppI18n();
  const router = useRouter();
  const params = useParams<{ taskId: string }>();
  const taskId = decodeURIComponent(Array.isArray(params?.taskId) ? params.taskId[0] : params?.taskId ?? '');
  const sessions = useChatStore((state) => state.sessions);
  const processRuns = useChatStore((state) => state.processRuns);
  const processEvents = useChatStore((state) => state.processEvents);
  const processArtifacts = useChatStore((state) => state.processArtifacts);
  const updateMessageFeedback = useChatStore((state) => state.updateMessageFeedback);

  const task = useMemo(
    () => buildTaskRuntimeView(taskId, { sessions, processRuns, processEvents, processArtifacts }, locale),
    [locale, processArtifacts, processEvents, processRuns, sessions, taskId]
  );
  const [backendTask, setBackendTask] = useState<BackendTask | null>(null);
  const [backendTaskLoading, setBackendTaskLoading] = useState(false);
  const [selectedRunId, setSelectedRunId] = useState<string | null>(task?.rootRunId ?? null);
  const [revision, setRevision] = useState('');
  const [runtimeFeedback, setRuntimeFeedback] = useState<TaskFeedbackItem | null>(null);
  const [actionError, setActionError] = useState<string | null>(null);
  const [actionBusy, setActionBusy] = useState<string | null>(null);

  React.useEffect(() => {
    setSelectedRunId(task?.rootRunId ?? null);
    setRuntimeFeedback(null);
  }, [task?.rootRunId]);

  React.useEffect(() => {
    let cancelled = false;
    if (task || !taskId) {
      setBackendTask(null);
      return () => {
        cancelled = true;
      };
    }
    setBackendTaskLoading(true);
    getBackendTask(taskId)
      .then((item) => {
        if (!cancelled) setBackendTask(item);
      })
      .catch(() => {
        if (!cancelled) setBackendTask(null);
      })
      .finally(() => {
        if (!cancelled) setBackendTaskLoading(false);
      });
    return () => {
      cancelled = true;
    };
  }, [task, taskId]);

  const runIds = useMemo(() => new Set(task?.tasks.map((item) => item.runId) ?? []), [task?.tasks]);
  const artifacts = useMemo(
    () => processArtifacts.filter((artifact) => runIds.has(artifact.run_id)),
    [processArtifacts, runIds]
  );
  const eventsByRun = useMemo(() => {
    const map = new Map<string, ProcessEvent[]>();
    for (const event of processEvents) {
      if (!runIds.has(event.run_id)) continue;
      map.set(event.run_id, [...(map.get(event.run_id) ?? []), event]);
    }
    return map;
  }, [processEvents, runIds]);
  const artifactsByRun = useMemo(() => {
    const map = new Map<string, ProcessArtifact[]>();
    for (const artifact of artifacts) {
      map.set(artifact.run_id, [...(map.get(artifact.run_id) ?? []), artifact]);
    }
    return map;
  }, [artifacts]);
  const phaseGroups = useMemo(() => {
    const groups = new Map<string, TaskRuntimeNodeView[]>();
    for (const item of task?.tasks ?? []) {
      const label = item.stageLabel || taskVisibleStatus(item, locale);
      groups.set(label, [...(groups.get(label) ?? []), item]);
    }
    return Array.from(groups.entries()).map(([label, nodes]) => ({ label, nodes }));
  }, [locale, task?.tasks]);
  const selectedNode = task?.tasks.find((item) => item.runId === selectedRunId) ?? task?.tasks[0] ?? null;

  const runAction = async (key: string, action: () => Promise<unknown>) => {
    setActionBusy(key);
    setActionError(null);
    try {
      await action();
    } catch (err: any) {
      setActionError(err.message || pickAppText(locale, '操作失败', 'Action failed'));
    } finally {
      setActionBusy(null);
    }
  };

  const deleteCurrentBackendTask = async () => {
    if (!backendTask) return;
    const title = backendTask.short_title || backendTask.description || backendTask.goal || backendTask.task_id;
    if (!window.confirm(pickAppText(locale, `删除任务“${title}”？`, `Delete task "${title}"?`))) {
      return;
    }
    await runAction('delete-backend-task', async () => {
      await deleteBackendTask(backendTask.task_id);
      router.push('/tasks');
    });
  };

  const backendFeedbackRunId = backendTask ? pickFeedbackRunId(backendTask) : null;

  if (!task && backendTask) {
    const validation = backendTask.validation_result;
    const accepted = Boolean(validation?.accepted);
    const validationIssues = [
      ...arrayOfStrings(validation?.issues),
      ...arrayOfStrings(validation?.missing_requirements),
    ];
    const feedbackItems = backendTask.feedback || [];
    return (
      <div className="mx-auto max-w-5xl space-y-6 p-6">
        <div className="flex flex-wrap items-center justify-between gap-3">
          <Button asChild variant="outline" className="w-fit">
            <Link href="/tasks">
              <ArrowLeft className="mr-2 h-4 w-4" />
              {pickAppText(locale, '返回任务列表', 'Back to tasks')}
            </Link>
          </Button>
          <div className="flex items-center gap-2">
            {backendTask.is_open ? <Badge variant="secondary">{pickAppText(locale, '进行中', 'Active')}</Badge> : null}
            <Badge>{humanTaskStatus(backendTask.status, locale)}</Badge>
            <Button
              variant="ghost"
              size="sm"
              className="text-destructive hover:text-destructive"
              disabled={Boolean(actionBusy)}
              onClick={() => void deleteCurrentBackendTask()}
            >
              <Trash2 className="mr-2 h-4 w-4" />
              {pickAppText(locale, '删除任务', 'Delete task')}
            </Button>
          </div>
        </div>

        <Card>
          <CardContent className="p-5">
            <h1 className="text-2xl font-semibold">{backendTask.short_title || String(backendTask.metadata?.short_title || '') || backendTask.description || backendTask.goal || backendTask.task_id}</h1>
            {backendTask.description ? (
              <p className="mt-2 max-w-3xl text-sm text-muted-foreground">{backendTask.description}</p>
            ) : null}
            <div className="mt-3 flex flex-wrap gap-x-4 gap-y-1 text-xs text-muted-foreground">
              <span>{pickAppText(locale, '来源会话', 'Session')}: {backendTask.session_id}</span>
              <span>{pickAppText(locale, '创建者', 'Creator')}: {backendTask.creator}</span>
              <span>{pickAppText(locale, '更新', 'Updated')}: {formatTaskRuntimeTime(backendTask.updated_at, locale)}</span>
            </div>
          </CardContent>
        </Card>

        <TaskFeedbackPanel
          sessionId={backendTask.session_id}
          runId={backendFeedbackRunId}
          taskStatus={backendTask.status}
          feedbackItems={feedbackItems}
          actionBusy={actionBusy}
          onSubmit={(feedbackType, comment) =>
            runAction(`backend-feedback-${feedbackType}`, async () => {
              await submitChatFeedback({
                sessionId: backendTask.session_id,
                runId: backendFeedbackRunId!,
                feedbackType,
                comment,
              });
              const refreshed = await getBackendTask(backendTask.task_id);
              setBackendTask(refreshed);
            })
          }
        />

        <Card>
          <CardHeader>
            <CardTitle className="text-base">{pickAppText(locale, 'Agent 执行过程', 'Agent conversation process')}</CardTitle>
          </CardHeader>
          <CardContent className="space-y-5">
            {(backendTask.runs ?? []).length === 0 ? (
              <div className="text-sm text-muted-foreground">{pickAppText(locale, '暂无可展示的问答过程', 'No readable conversation process yet')}</div>
            ) : (
              (backendTask.runs ?? []).map((run, index) => <BackendRunConversation key={run.run_id} run={run} index={index} />)
            )}
          </CardContent>
        </Card>

        <Card>
          <CardHeader>
            <CardTitle className="text-base">{pickAppText(locale, '验证和反馈', 'Validation and feedback')}</CardTitle>
          </CardHeader>
          <CardContent className="space-y-4 text-sm">
            <div className="rounded-lg border border-border bg-muted/25 p-4">
              <div className="flex items-center gap-2">
                {validation ? (
                  accepted ? <CheckCircle2 className="h-5 w-5 text-[#657162]" /> : <XCircle className="h-5 w-5 text-destructive" />
                ) : (
                  <HelpCircle className="h-5 w-5 text-muted-foreground" />
                )}
                <div className="font-medium">
                  {validation
                    ? accepted
                      ? pickAppText(locale, '验证通过', 'Validation passed')
                      : pickAppText(locale, '需要继续修改', 'Needs revision')
                    : pickAppText(locale, '尚未验证', 'Not validated yet')}
                </div>
              </div>
              {validation ? (
                <div className="mt-2 text-muted-foreground">
                  {pickAppText(locale, '评分', 'Score')}: {String(validation.score ?? '-')} · {pickAppText(locale, '验证器', 'Validator')}: {String(validation.validator ?? '-')}
                </div>
              ) : null}
              {validationIssues.length > 0 && (
                <ul className="mt-3 list-disc space-y-1 pl-5 text-muted-foreground">
                  {validationIssues.map((item, index) => <li key={`${item}:${index}`}>{item}</li>)}
                </ul>
              )}
              {typeof validation?.recommended_revision_prompt === 'string' && validation.recommended_revision_prompt && (
                <p className="mt-3 rounded-md bg-background p-3 text-muted-foreground">{validation.recommended_revision_prompt}</p>
              )}
            </div>

            <div className="space-y-2">
              <div className="font-medium">{pickAppText(locale, '用户反馈', 'User feedback')}</div>
              {feedbackItems.length === 0 ? (
                <p className="text-muted-foreground">{pickAppText(locale, '还没有用户反馈。', 'No user feedback yet.')}</p>
              ) : (
                feedbackItems.map((item, index) => (
                  <div key={index} className="rounded-md border border-border p-3">
                    <div className="font-medium">{humanFeedback(String(item.feedback_type || ''), locale)}</div>
                    {item.comment ? <p className="mt-1 text-muted-foreground">{String(item.comment)}</p> : null}
                    {item.created_at ? <p className="mt-1 text-xs text-muted-foreground">{formatTaskRuntimeTime(String(item.created_at), locale)}</p> : null}
                  </div>
                ))
              )}
            </div>
          </CardContent>
        </Card>
      </div>
    );
  }

  if (!task) {
    return (
      <div className="mx-auto flex max-w-4xl flex-col gap-4 p-6">
        <Button asChild variant="outline" className="w-fit">
          <Link href="/tasks">
            <ArrowLeft className="mr-2 h-4 w-4" />
            {pickAppText(locale, '返回任务列表', 'Back to tasks')}
          </Link>
        </Button>
        <Card className="border-dashed">
          <CardContent className="py-16 text-center">
            <h1 className="text-2xl font-semibold">{pickAppText(locale, '任务不存在', 'Task not found')}</h1>
            <p className="mt-2 text-sm text-muted-foreground">
              {backendTaskLoading
                ? pickAppText(locale, '正在从后端任务库加载任务。', 'Loading the task from the backend task store.')
                : pickAppText(locale, '当前前端状态和后端任务库里都没有这个任务。', 'Neither frontend state nor backend task store contains this task.')}
            </p>
          </CardContent>
        </Card>
      </div>
    );
  }

  const progressValue = progressPercent(task.progress.value, task.progress.max);

  return (
    <div className="mx-auto max-w-7xl space-y-6 p-6">
      <div className="flex flex-wrap items-center justify-between gap-3">
        <div className="flex flex-wrap items-center gap-2">
          <Button asChild variant="outline" size="sm">
            <Link href="/tasks">
              <ArrowLeft className="mr-2 h-4 w-4" />
              {pickAppText(locale, '返回任务', 'Back to tasks')}
            </Link>
          </Button>
          <Button asChild variant="ghost" size="sm">
            <Link href="/">
              <MessageSquare className="mr-2 h-4 w-4" />
              {pickAppText(locale, '对话', 'Chat')}
            </Link>
          </Button>
        </div>
      </div>

      <Card>
        <CardContent className="p-5">
          <div className="flex flex-col gap-5 lg:flex-row lg:items-start lg:justify-between">
            <div className="min-w-0">
              <div className="flex flex-wrap items-center gap-3">
                <h1 className="truncate text-2xl font-semibold">{task.title}</h1>
                <TaskRuntimeStatusBadge status={task.status} />
              </div>
              <div className="mt-3 flex flex-wrap gap-x-4 gap-y-1 text-xs text-muted-foreground">
                <span>{pickAppText(locale, '来源会话', 'Session')}: {task.sourceSessionLabel}</span>
                <span>{pickAppText(locale, '主 Agent', 'Lead agent')}: {task.rootActorName}</span>
                <span>{pickAppText(locale, '开始', 'Started')}: {formatTaskRuntimeTime(task.createdAt, locale)}</span>
                <span>{pickAppText(locale, '耗时', 'Duration')}: {formatTaskRuntimeDuration(task.durationMs, locale)}</span>
              </div>
            </div>
            <div className="grid w-full gap-3 sm:grid-cols-4 lg:w-[520px]">
              <Metric label={pickAppText(locale, '节点', 'Nodes')} value={String(task.stats.totalRuns)} />
              <Metric label={pickAppText(locale, '活跃', 'Active')} value={String(task.stats.activeRuns)} />
              <Metric label={pickAppText(locale, '产物', 'Artifacts')} value={String(task.stats.artifactCount)} />
              <Metric label={pickAppText(locale, '异常', 'Alerts')} value={String(task.stats.alertCount)} />
            </div>
          </div>
          <div className="mt-5 space-y-2">
            <div className="flex justify-between text-sm">
              <span className="text-muted-foreground">{task.progress.label}</span>
              <span className="font-medium">{progressValue}%</span>
            </div>
            <div className="h-2 overflow-hidden rounded-full bg-secondary">
              <div className="h-full bg-primary" style={{ width: `${progressValue}%` }} />
            </div>
          </div>
        </CardContent>
      </Card>

      {actionError && (
        <Card className="border-destructive">
          <CardContent className="flex items-center gap-2 pt-6 text-sm text-destructive">
            <AlertCircle className="h-4 w-4" />
            {actionError}
          </CardContent>
        </Card>
      )}

      <div className="grid gap-6 xl:grid-cols-[minmax(0,1fr)_360px]">
        <div className="space-y-6">
          <Card>
            <CardHeader>
              <CardTitle className="text-base">{pickAppText(locale, '阶段链', 'Phase chain')}</CardTitle>
            </CardHeader>
            <CardContent>
              <div className="flex flex-wrap gap-2">
                {phaseGroups.map((phase, index) => (
                  <div key={`${phase.label}:${index}`} className="flex items-center gap-2">
                    <div className="rounded-md border border-border bg-muted/35 px-3 py-2 text-sm">
                      <div className="font-medium">{phase.label}</div>
                      <div className="text-xs text-muted-foreground">{phase.nodes.length} nodes</div>
                    </div>
                    {index < phaseGroups.length - 1 ? <span className="text-muted-foreground">/</span> : null}
                  </div>
                ))}
              </div>
            </CardContent>
          </Card>

          {phaseGroups.map((phase) => (
            <Card key={phase.label}>
              <CardHeader>
                <CardTitle className="text-base">{phase.label}</CardTitle>
              </CardHeader>
              <CardContent className="grid gap-3 md:grid-cols-2">
                {phase.nodes.map((node) => (
                  <button
                    key={node.runId}
                    type="button"
                    onClick={() => setSelectedRunId(node.runId)}
                    className={`rounded-md border p-4 text-left transition-colors ${selectedRunId === node.runId ? 'border-primary bg-accent/45' : 'border-border bg-card hover:bg-muted/40'}`}
                  >
                    <div className="flex items-start justify-between gap-3">
                      <div className="min-w-0">
                        <div className="truncate font-medium">{node.title}</div>
                        <div className="mt-1 text-xs text-muted-foreground">{node.actorName}</div>
                      </div>
                      <TaskRuntimeStatusBadge status={node.status} />
                    </div>
                    <div className="mt-3 text-sm text-muted-foreground">
                      {node.summary || taskVisibleStatus(node, locale)}
                    </div>
                    <div className="mt-3 flex gap-3 text-xs text-muted-foreground">
                      <span>{pickAppText(locale, '子节点', 'Children')}: {node.childTaskIds.length}</span>
                      <span>{pickAppText(locale, '节点结果', 'Node results')}: {(artifactsByRun.get(node.runId) ?? []).length}</span>
                    </div>
                  </button>
                ))}
              </CardContent>
            </Card>
          ))}
        </div>

        <div className="space-y-6">
          <Card>
            <CardHeader>
              <CardTitle className="text-base">{pickAppText(locale, '节点详情', 'Node detail')}</CardTitle>
            </CardHeader>
            <CardContent>
              {selectedNode ? (
                <div className="space-y-4">
                  <div>
                    <div className="font-medium">{selectedNode.title}</div>
                    <div className="mt-1 text-xs text-muted-foreground">{selectedNode.runId}</div>
                  </div>
                  <TaskRuntimeStatusBadge status={selectedNode.status} />
                  <p className="text-sm text-muted-foreground">{selectedNode.summary || '-'}</p>
                  <div className="space-y-2">
                    {(eventsByRun.get(selectedNode.runId) ?? []).slice(-5).map((event) => (
                      <div key={event.event_id} className="rounded-md border border-border bg-muted/30 p-2 text-xs">
                        <div className="font-medium">{event.kind}</div>
                        <div className="mt-1 text-muted-foreground">{event.text || formatTaskRuntimeTime(event.created_at, locale)}</div>
                      </div>
                    ))}
                  </div>
                </div>
              ) : (
                <p className="text-sm text-muted-foreground">-</p>
              )}
            </CardContent>
          </Card>

          <TaskFeedbackPanel
            sessionId={task.sessionId || 'web:default'}
            runId={task.rootRunId}
            taskStatus={task.status}
            feedbackItems={runtimeFeedback ? [runtimeFeedback] : []}
            actionBusy={actionBusy}
            revision={revision}
            onRevisionChange={setRevision}
            onSubmit={(feedbackType, comment) =>
              runAction(`runtime-feedback-${feedbackType}`, async () => {
                updateMessageFeedback(task.rootRunId, feedbackType);
                await submitChatFeedback({
                  sessionId: task.sessionId || 'web:default',
                  runId: task.rootRunId,
                  feedbackType,
                  comment,
                });
                setRuntimeFeedback({
                  feedback_type: feedbackType,
                  comment: comment || '',
                  created_at: new Date().toISOString(),
                  run_id: task.rootRunId,
                });
                setRevision('');
              })
            }
          />

          <Card>
            <CardHeader>
              <div className="flex items-center justify-between gap-3">
                <CardTitle className="text-base">{pickAppText(locale, '产物', 'Artifacts')}</CardTitle>
                <Button
                  size="sm"
                  variant="outline"
                  disabled={artifacts.length === 0}
                  onClick={() => downloadText(`${task.taskId}-artifacts.json`, JSON.stringify(artifacts, null, 2))}
                >
                  <Download className="mr-2 h-4 w-4" />
                  {pickAppText(locale, '全部下载', 'Download all')}
                </Button>
              </div>
            </CardHeader>
            <CardContent className="space-y-2">
              {artifacts.length === 0 ? (
                <p className="text-sm text-muted-foreground">{pickAppText(locale, '暂无产物', 'No artifacts yet')}</p>
              ) : (
                artifacts.map((artifact) => (
                  <div key={artifact.artifact_id} className="flex items-center justify-between gap-3 rounded-md border border-border p-3">
                    <div className="min-w-0">
                      <div className="flex items-center gap-2 text-sm font-medium">
                        <FileText className="h-4 w-4 text-muted-foreground" />
                        <span className="truncate">{artifact.title}</span>
                      </div>
                      <div className="mt-1 text-xs text-muted-foreground">{artifact.actor_name || artifact.actor_id}</div>
                    </div>
                    {artifact.url || artifact.file_id ? (
                      <Button asChild size="sm" variant="outline">
                        <a href={artifact.url || getFileUrl(artifact.file_id!)} target="_blank" rel="noopener noreferrer">
                          <Download className="mr-2 h-3.5 w-3.5" />
                          {pickAppText(locale, '下载', 'Download')}
                        </a>
                      </Button>
                    ) : (
                      <Button
                        size="sm"
                        variant="outline"
                        onClick={() => downloadText(`${artifact.title || artifact.artifact_id}.txt`, artifact.content || JSON.stringify(artifact.data ?? {}, null, 2))}
                      >
                        <Download className="mr-2 h-3.5 w-3.5" />
                        {pickAppText(locale, '下载', 'Download')}
                      </Button>
                    )}
                  </div>
                ))
              )}
            </CardContent>
          </Card>
        </div>
      </div>
    </div>
  );
}

function Metric({ label, value }: { label: string; value: string }) {
  return (
    <div className="rounded-md border border-border bg-muted/30 px-3 py-3">
      <div className="text-xs text-muted-foreground">{label}</div>
      <div className="mt-1 text-lg font-semibold">{value}</div>
    </div>
  );
}

function TaskFeedbackPanel({
  sessionId,
  runId,
  taskStatus,
  feedbackItems,
  actionBusy,
  revision,
  onRevisionChange,
  onSubmit,
}: {
  sessionId: string;
  runId: string | null;
  taskStatus: string;
  feedbackItems: TaskFeedbackItem[];
  actionBusy: string | null;
  revision?: string;
  onRevisionChange?: (value: string) => void;
  onSubmit: (feedbackType: TaskFeedbackType, comment?: string) => Promise<unknown>;
}) {
  const { locale } = useAppI18n();
  const [localComment, setLocalComment] = React.useState('');
  const comment = revision ?? localComment;
  const setComment = onRevisionChange ?? setLocalComment;
  const isFinalized = taskStatus === 'closed' || taskStatus === 'abandoned';
  const recordedFeedback = feedbackForRun(feedbackItems, runId) ?? (isFinalized ? latestFeedback(feedbackItems) : null);
  const canSubmit = Boolean(runId) && !recordedFeedback && !isFinalized && !actionBusy;

  const submit = (feedbackType: TaskFeedbackType, nextComment?: string) => {
    if (!runId || !canSubmit) return;
    void onSubmit(feedbackType, nextComment);
  };

  return (
    <Card>
      <CardHeader>
        <CardTitle className="text-base">{pickAppText(locale, '任务反馈', 'Task feedback')}</CardTitle>
      </CardHeader>
      <CardContent className="space-y-4">
        {recordedFeedback ? (
          <div className="rounded-md border border-border bg-muted/25 p-3 text-sm">
            <div className="flex items-center gap-2 font-medium">
              <CheckCircle2 className="h-4 w-4 text-[#657162]" />
              {pickAppText(locale, '已提交反馈', 'Feedback submitted')}: {humanFeedback(String(recordedFeedback.feedback_type || ''), locale)}
            </div>
            {recordedFeedback.comment ? (
              <p className="mt-2 text-muted-foreground">{String(recordedFeedback.comment)}</p>
            ) : null}
            {recordedFeedback.created_at ? (
              <p className="mt-2 text-xs text-muted-foreground">{formatTaskRuntimeTime(String(recordedFeedback.created_at), locale)}</p>
            ) : null}
          </div>
        ) : isFinalized ? (
          <div className="rounded-md border border-border bg-muted/25 p-3 text-sm text-muted-foreground">
            {pickAppText(locale, '任务已结束，不能再提交新的反馈。', 'This task is finalized and cannot accept new feedback.')}
          </div>
        ) : !runId ? (
          <div className="rounded-md border border-border bg-muted/25 p-3 text-sm text-muted-foreground">
            {pickAppText(locale, '暂无可反馈的运行记录。', 'No run is available for feedback yet.')}
          </div>
        ) : null}

        <div className="grid gap-2 sm:grid-cols-3">
          <FeedbackButton
            type="satisfied"
            icon={<ThumbsUp className="mr-2 h-4 w-4" />}
            label={pickAppText(locale, '满意', 'Satisfied')}
            actionBusy={actionBusy}
            disabled={!canSubmit}
            onClick={() => submit('satisfied', comment.trim() || undefined)}
          />
          <FeedbackButton
            type="revise"
            icon={<RefreshCw className="mr-2 h-4 w-4" />}
            label={pickAppText(locale, '需要修改', 'Needs revision')}
            actionBusy={actionBusy}
            disabled={!canSubmit || !comment.trim()}
            onClick={() => submit('revise', comment.trim())}
          />
          <FeedbackButton
            type="abandon"
            icon={<XCircle className="mr-2 h-4 w-4" />}
            label={pickAppText(locale, '放弃', 'Abandon')}
            actionBusy={actionBusy}
            disabled={!canSubmit}
            onClick={() => submit('abandon', comment.trim() || undefined)}
          />
        </div>

        <Textarea
          value={comment}
          onChange={(event) => setComment(event.target.value)}
          disabled={Boolean(recordedFeedback) || isFinalized || Boolean(actionBusy)}
          placeholder={pickAppText(locale, '需要修改时写下具体要求；满意或放弃可选填说明。', 'Describe requested changes; notes are optional for satisfied or abandon.')}
        />
        <div className="text-xs text-muted-foreground">
          {pickAppText(locale, '反馈将记录到当前任务运行：', 'Feedback will be recorded on run: ')}
          <span className="font-mono">{runId || '-'}</span>
          <span className="mx-1">·</span>
          {pickAppText(locale, '会话：', 'Session: ')}
          <span className="font-mono">{sessionId}</span>
        </div>
      </CardContent>
    </Card>
  );
}

function FeedbackButton({
  type,
  icon,
  label,
  actionBusy,
  disabled,
  onClick,
}: {
  type: TaskFeedbackType;
  icon: React.ReactNode;
  label: string;
  actionBusy: string | null;
  disabled: boolean;
  onClick: () => void;
}) {
  const isBusy = Boolean(actionBusy?.endsWith(type));
  return (
    <Button type="button" variant="outline" className="w-full justify-center" disabled={disabled || Boolean(actionBusy)} onClick={onClick}>
      {isBusy ? <Loader2 className="mr-2 h-4 w-4 animate-spin" /> : icon}
      {label}
    </Button>
  );
}

function BackendRunConversation({ run, index }: { run: BackendTaskRun; index: number }) {
  const { locale } = useAppI18n();
  return (
    <div className="rounded-lg border border-border bg-background p-4">
      <div className="mb-4 flex flex-wrap items-center justify-between gap-3">
        <div>
          <div className="font-medium">{run.title || pickAppText(locale, `Agent ${index + 1}`, `Agent ${index + 1}`)}</div>
          <div className="mt-1 text-xs text-muted-foreground">
            {run.started_at ? formatTaskRuntimeTime(run.started_at, locale) : pickAppText(locale, '时间未知', 'Unknown time')}
            {run.finish_reason ? ` · ${humanFinishReason(run.finish_reason, locale)}` : ''}
          </div>
        </div>
        <Badge variant={run.success === false ? 'destructive' : 'secondary'}>
          {run.success === false ? pickAppText(locale, '失败', 'Failed') : pickAppText(locale, '已完成', 'Done')}
        </Badge>
      </div>

      {run.messages.length === 0 ? (
        <p className="text-sm text-muted-foreground">{run.task_text || pickAppText(locale, '这次运行没有可见对话消息。', 'This run has no visible conversation messages.')}</p>
      ) : (
        <div className="space-y-3">
          {run.messages.map((message, messageIndex) => {
            const isAssistant = message.role === 'assistant';
            const isTool = message.role === 'tool';
            const Icon = isAssistant ? Bot : isTool ? FileText : User;
            return (
              <div key={`${message.role}:${message.created_at}:${messageIndex}`} className="flex gap-3">
                <div className="mt-1 flex h-7 w-7 shrink-0 items-center justify-center rounded-full bg-muted">
                  <Icon className="h-4 w-4 text-muted-foreground" />
                </div>
                <div className="min-w-0 flex-1">
                  <div className="mb-1 flex items-center gap-2 text-xs text-muted-foreground">
                    <span>{isAssistant ? run.title || pickAppText(locale, 'Agent 回复', 'Agent reply') : isTool ? message.tool_name || pickAppText(locale, '工具结果', 'Tool result') : pickAppText(locale, '用户要求', 'User request')}</span>
                    {message.created_at ? <span>{formatTaskRuntimeTime(message.created_at, locale)}</span> : null}
                  </div>
                  <div className="whitespace-pre-wrap rounded-md border border-border bg-muted/20 px-3 py-2 text-sm leading-6">
                    {message.content}
                  </div>
                </div>
              </div>
            );
          })}
        </div>
      )}
    </div>
  );
}

function humanTaskStatus(status: string, locale: 'zh-CN' | 'en-US') {
  const map: Record<string, [string, string]> = {
    open: ['已创建', 'Open'],
    running: ['执行中', 'Running'],
    validating: ['验证中', 'Validating'],
    awaiting_feedback: ['等待反馈', 'Awaiting feedback'],
    needs_revision: ['需要修改', 'Needs revision'],
    closed: ['已完成', 'Closed'],
    abandoned: ['已放弃', 'Abandoned'],
  };
  const item = map[status];
  return item ? pickAppText(locale, item[0], item[1]) : status;
}

function humanFeedback(type: string, locale: 'zh-CN' | 'en-US') {
  if (type === 'satisfied') return pickAppText(locale, '满意', 'Satisfied');
  if (type === 'revise') return pickAppText(locale, '请求修改', 'Revision requested');
  if (type === 'abandon') return pickAppText(locale, '放弃任务', 'Abandoned');
  return type || pickAppText(locale, '反馈', 'Feedback');
}

function humanFinishReason(reason: string, locale: 'zh-CN' | 'en-US') {
  if (reason === 'stop') return pickAppText(locale, '正常结束', 'Completed');
  if (reason === 'error') return pickAppText(locale, '执行出错', 'Error');
  if (reason === 'cancelled') return pickAppText(locale, '已取消', 'Cancelled');
  return reason;
}

function pickFeedbackRunId(task: BackendTask): string | null {
  const runIds = task.run_ids.filter(Boolean);
  if (runIds.length > 0) return runIds[runIds.length - 1];
  const runs = task.runs ?? [];
  if (runs.length > 0) return runs[runs.length - 1].run_id;
  return null;
}

function feedbackForRun(items: TaskFeedbackItem[], runId: string | null): TaskFeedbackItem | null {
  if (!runId) return null;
  const ordered = [...items].reverse();
  return ordered.find((item) => String(item.run_id || '') === runId) ?? null;
}

function latestFeedback(items: TaskFeedbackItem[]): TaskFeedbackItem | null {
  return [...items].reverse()[0] ?? null;
}

function arrayOfStrings(value: unknown): string[] {
  return Array.isArray(value) ? value.map((item) => String(item)).filter(Boolean) : [];
}
