'use client';

import React from 'react';
import Link from 'next/link';
import { Bot, CheckCircle2, ChevronRight, Loader2, Paperclip, RefreshCcw, ThumbsUp, User, XCircle } from 'lucide-react';

import type { ChatMessage, ProcessArtifact, ProcessEvent, ProcessRun } from '@/types';
import { getAccessToken, getFileUrl } from '@/lib/api';
import { getTaskCardMessageIndexes, hasVisibleChatContent, normalizedMessageText, shouldDisplayChatMessage } from '@/lib/chat-messages';
import { AgentTeamBlock } from '@/components/chat-workbench/AgentTeamBlock';
import { MarkdownContent } from '@/components/chat-workbench/MarkdownContent';
import { ScrollArea } from '@/components/ui/scroll-area';
import {
  Dialog,
  DialogContent,
  DialogDescription,
  DialogFooter,
  DialogHeader,
  DialogTitle,
} from '@/components/ui/dialog';
import { pickAppText } from '@/lib/i18n/core';
import { useAppI18n } from '@/lib/i18n/provider';
import { containedPreservedLongTextClass } from '@/lib/text-wrapping';

function AuthImage({ src, alt, className }: { src: string; alt: string; className?: string }) {
  const [blobUrl, setBlobUrl] = React.useState<string | null>(null);

  React.useEffect(() => {
    const token = getAccessToken();
    const headers: Record<string, string> = {};
    if (token) headers.Authorization = `Bearer ${token}`;

    let revoke: string | null = null;
    fetch(src, { headers })
      .then((res) => res.blob())
      .then((blob) => {
        revoke = URL.createObjectURL(blob);
        setBlobUrl(revoke);
      })
      .catch(() => {});

    return () => {
      if (revoke) URL.revokeObjectURL(revoke);
    };
  }, [src]);

  if (!blobUrl) return <div className="w-32 h-32 bg-muted animate-pulse rounded" />;
  return <img src={blobUrl} alt={alt} className={className} loading="lazy" decoding="async" />;
}

function MessageBubble({
  message,
  showTaskCard,
  canSendFeedback,
  onFeedback,
  onRequestRevision,
}: {
  message: ChatMessage;
  showTaskCard: boolean;
  canSendFeedback: boolean;
  onFeedback: (runId: string, feedbackType: 'accept' | 'revise' | 'abandon', comment?: string) => void;
  onRequestRevision: (runId: string) => void;
}) {
  const { locale } = useAppI18n();
  const isUser = message.role === 'user';
  const textContent = normalizedMessageText(message.content);
  const [feedbackMode, setFeedbackMode] = React.useState<'accept' | null>(null);
  const [feedbackComment, setFeedbackComment] = React.useState('');
  const [confirmAbandonOpen, setConfirmAbandonOpen] = React.useState(false);
  const feedbackTextareaId = message.run_id ? `feedback-note-${message.run_id}` : undefined;

  return (
    <div className={`flex gap-3 ${isUser ? 'justify-end' : ''}`}>
      {!isUser && (
        <div className="mt-1 flex h-7 w-7 flex-shrink-0 items-center justify-center rounded-full bg-[#F1EFEE]">
          <Bot className="w-4 h-4 text-primary" />
        </div>
      )}
      <div
        className={`min-w-0 max-w-[88%] px-4 py-3 ${
          isUser
            ? 'rounded-[28px] bg-primary text-primary-foreground'
            : 'rounded-none bg-transparent text-[#1D1715]'
        }`}
      >
        {message.attachments && message.attachments.length > 0 && (
          <div className="mb-2 space-y-2">
            {message.attachments.map((att) => {
              const fileUrl = getFileUrl(att.file_id);
              if (att.content_type.startsWith('image/')) {
                return (
                  <a key={att.file_id} href={fileUrl} target="_blank" rel="noopener noreferrer">
                    <AuthImage
                      src={fileUrl}
                      alt={att.name}
                      className="max-w-xs max-h-60 rounded border border-border/50 cursor-pointer hover:opacity-90"
                    />
                  </a>
                );
              }
              return (
                <a
                  key={att.file_id}
                  href={fileUrl}
                  download={att.name}
                  className={`flex min-w-0 items-center gap-2 px-3 py-2 rounded-md text-sm ${
                    isUser
                      ? 'bg-primary-foreground/10 hover:bg-primary-foreground/20'
                      : 'bg-muted hover:bg-muted/80'
                  }`}
                >
                  <Paperclip className="w-3.5 h-3.5 flex-shrink-0" />
                  <span className="min-w-0 truncate">{att.name}</span>
                  {att.size && (
                    <span className="text-xs opacity-70 flex-shrink-0">
                      {att.size > 1024 * 1024
                        ? `${(att.size / 1024 / 1024).toFixed(1)}MB`
                        : `${(att.size / 1024).toFixed(0)}KB`}
                    </span>
                  )}
                </a>
              );
            })}
          </div>
        )}

        {isUser ? (
          <p className={`text-sm ${containedPreservedLongTextClass}`}>{textContent}</p>
        ) : (
          <MarkdownContent content={textContent} />
        )}
        {!isUser && showTaskCard && message.task_id && (
          <div className="mt-3 rounded-md border border-border bg-muted/35 p-3">
            <div className="flex flex-wrap items-center justify-between gap-3">
              <div className="min-w-0">
                <div className="text-xs font-medium uppercase text-muted-foreground">Task</div>
                <div className="mt-1 truncate text-sm font-medium">
                  {pickAppText(locale, '已创建任务', 'Task created')}: {message.task_id}
                </div>
              </div>
              <Link
                href={`/tasks/${encodeURIComponent(message.task_id)}`}
                className="inline-flex h-11 items-center gap-1 rounded-md bg-primary px-3 text-xs font-medium text-primary-foreground hover:bg-primary/90"
              >
                {pickAppText(locale, '查看任务', 'Open task')}
                <ChevronRight className="h-3.5 w-3.5" />
              </Link>
            </div>
          </div>
        )}
        {!isUser && (canSendFeedback || message.feedback_state) && message.run_id && (
          <div className="mt-3 space-y-2 border-t border-border/70 pt-3">
            {message.feedback_state ? (
              <div className="flex items-center gap-2 text-xs text-muted-foreground">
                <CheckCircle2 className="h-3.5 w-3.5" />
                <span>
                  {message.feedback_state === 'accept' || message.feedback_state === 'satisfied'
                    ? pickAppText(locale, '已接受', 'Accepted')
                    : message.feedback_state === 'revise'
                      ? pickAppText(locale, '已请求修改', 'Revision requested')
                      : pickAppText(locale, '已放弃任务', 'Task abandoned')}
                </span>
              </div>
            ) : (
              <>
                <div className="flex flex-wrap items-center gap-2">
                  <button
                    type="button"
                    onClick={() => setFeedbackMode('accept')}
                    className="inline-flex h-11 items-center gap-1 rounded-md border border-border px-3 text-xs text-muted-foreground hover:bg-accent hover:text-foreground"
                  >
                    <ThumbsUp className="h-3.5 w-3.5" />
                    {pickAppText(locale, '接受', 'Accept')}
                  </button>
                  <button
                    type="button"
                    onClick={() => onRequestRevision(message.run_id!)}
                    className="inline-flex h-11 items-center gap-1 rounded-md border border-border px-3 text-xs text-muted-foreground hover:bg-accent hover:text-foreground"
                  >
                    <RefreshCcw className="h-3.5 w-3.5" />
                    {pickAppText(locale, '需要修改', 'Revise')}
                  </button>
                  <button
                    type="button"
                    onClick={() => setConfirmAbandonOpen(true)}
                    className="inline-flex h-11 items-center gap-1 rounded-md border border-border px-3 text-xs text-muted-foreground hover:bg-accent hover:text-foreground"
                  >
                    <XCircle className="h-3.5 w-3.5" />
                    {pickAppText(locale, '放弃', 'Abandon')}
                  </button>
                </div>
                {feedbackMode && (
                  <div className="space-y-2 rounded-md border border-border bg-background p-2">
                    <label htmlFor={feedbackTextareaId} className="sr-only">
                      {pickAppText(locale, '接受反馈备注', 'Acceptance note')}
                    </label>
                    <textarea
                      id={feedbackTextareaId}
                      value={feedbackComment}
                      onChange={(event) => setFeedbackComment(event.target.value)}
                      placeholder={pickAppText(locale, '可选：补充说明...', 'Optional note...')}
                      className="min-h-20 w-full resize-none rounded-md border border-input bg-background px-3 py-2 text-sm outline-none focus:ring-2 focus:ring-ring"
                    />
                    <div className="flex justify-end gap-2">
                      <button
                        type="button"
                        onClick={() => {
                          setFeedbackMode(null);
                          setFeedbackComment('');
                        }}
                        className="h-11 rounded-md border border-border px-3 text-xs text-muted-foreground hover:bg-accent"
                      >
                        {pickAppText(locale, '取消', 'Cancel')}
                      </button>
                      <button
                        type="button"
                        onClick={() => onFeedback(message.run_id!, feedbackMode, feedbackComment.trim() || undefined)}
                        className="h-11 rounded-md bg-primary px-3 text-xs font-medium text-primary-foreground hover:bg-primary/90"
                      >
                        {pickAppText(locale, '提交', 'Submit')}
                      </button>
                    </div>
                  </div>
                )}
              </>
            )}
            <Dialog open={confirmAbandonOpen} onOpenChange={setConfirmAbandonOpen}>
              <DialogContent className="max-w-[calc(100vw-2rem)] sm:max-w-md">
                <DialogHeader>
                  <DialogTitle>{pickAppText(locale, '放弃当前任务？', 'Abandon this task?')}</DialogTitle>
                  <DialogDescription>
                    {pickAppText(locale, '放弃后会停止等待该任务的验收结果，此操作需要明确确认。', 'This stops waiting for this task acceptance result and requires confirmation.')}
                  </DialogDescription>
                </DialogHeader>
                <DialogFooter className="gap-2 sm:gap-2">
                  <button
                    type="button"
                    onClick={() => setConfirmAbandonOpen(false)}
                    className="h-11 rounded-md border border-border px-4 text-sm text-muted-foreground hover:bg-accent"
                  >
                    {pickAppText(locale, '取消', 'Cancel')}
                  </button>
                  <button
                    type="button"
                    onClick={() => {
                      setConfirmAbandonOpen(false);
                      onFeedback(message.run_id!, 'abandon');
                    }}
                    className="h-11 rounded-md bg-destructive px-4 text-sm font-medium text-destructive-foreground hover:bg-destructive/90"
                  >
                    {pickAppText(locale, '确认放弃', 'Confirm abandon')}
                  </button>
                </DialogFooter>
              </DialogContent>
            </Dialog>
            {message.feedback_error && (
              <span className="text-xs text-destructive">{message.feedback_error}</span>
            )}
          </div>
        )}
      </div>
      {isUser && (
        <div className="mt-1 flex h-7 w-7 flex-shrink-0 items-center justify-center rounded-full bg-secondary">
          <User className="w-4 h-4" />
        </div>
      )}
    </div>
  );
}

type AgentTeamGroup = {
  rootRun: ProcessRun;
  memberRuns: ProcessRun[];
  startedAt: string;
};

const TERMINAL_RUN_STATUSES = new Set<ProcessRun['status']>(['done', 'error', 'cancelled']);

function shouldHideSystemAgentMessage(message: ChatMessage): boolean {
  if (message.role !== 'assistant' || typeof message.content !== 'string') {
    return false;
  }

  const content = message.content.trim();
  return (
    /^\[(Agent team|Subagent)\s+['"][^'"]+['"]\s+(completed|failed|cancelled|finished)\]/i.test(content)
      || (content.startsWith('[Agent team ') && content.includes('\nTask:'))
  );
}

function hasRenderableMessageContent(message: ChatMessage): boolean {
  return hasVisibleChatContent(message);
}

function shouldHideMessage(message: ChatMessage): boolean {
  if (shouldHideSystemAgentMessage(message)) {
    return true;
  }
  return !shouldDisplayChatMessage(message);
}

function parseTimelineTime(value?: string | null): number | null {
  if (!value) return null;
  const parsed = new Date(value).getTime();
  return Number.isFinite(parsed) ? parsed : null;
}

function buildAgentTeamGroups(processRuns: ProcessRun[]): AgentTeamGroup[] {
  const runMap = new Map(processRuns.map((run) => [run.run_id, run]));
  const groups = new Map<string, AgentTeamGroup>();

  for (const run of processRuns) {
    if (run.actor_type !== 'agent') {
      continue;
    }

    let root = run;
    const seen = new Set<string>([run.run_id]);
    let parentId = run.parent_run_id ?? null;
    while (parentId) {
      const parent = runMap.get(parentId);
      if (!parent || seen.has(parent.run_id)) {
        break;
      }
      root = parent;
      seen.add(parent.run_id);
      parentId = parent.parent_run_id ?? null;
    }

    const existing = groups.get(root.run_id);
    if (existing) {
      existing.memberRuns.push(run);
      continue;
    }
    groups.set(root.run_id, {
      rootRun: root,
      memberRuns: [run],
      startedAt: root.started_at || run.started_at,
    });
  }

  return Array.from(groups.values())
    .map((group) => ({
      ...group,
      memberRuns: [...group.memberRuns].sort((a: ProcessRun, b: ProcessRun) => {
        const at = parseTimelineTime(a.started_at) ?? 0;
        const bt = parseTimelineTime(b.started_at) ?? 0;
        return at - bt;
      }),
    }))
    .sort((a, b) => {
      const at = parseTimelineTime(a.startedAt) ?? 0;
      const bt = parseTimelineTime(b.startedAt) ?? 0;
      return at - bt;
    });
}

export function MessageList({
  messages,
  isThinking,
  messagesEndRef,
  viewportRef,
  processRuns,
  processEvents,
  processArtifacts,
  selectedRunId,
  onSelectRun,
  onFeedback,
  onRequestRevision,
}: {
  messages: ChatMessage[];
  isThinking: boolean;
  messagesEndRef: React.RefObject<HTMLDivElement>;
  viewportRef: React.RefObject<HTMLDivElement>;
  processRuns: ProcessRun[];
  processEvents: ProcessEvent[];
  processArtifacts: ProcessArtifact[];
  selectedRunId: string | null;
  onSelectRun: (runId: string) => void;
  onFeedback: (runId: string, feedbackType: 'accept' | 'revise' | 'abandon', comment?: string) => void;
  onRequestRevision: (runId: string) => void;
}) {
  const { locale } = useAppI18n();
  const visibleMessages = React.useMemo(
    () => messages.filter((message) => !shouldHideMessage(message)),
    [messages]
  );
  const teamGroups = React.useMemo(
    () =>
      buildAgentTeamGroups(processRuns).filter((group) =>
        group.memberRuns.some((run) => !TERMINAL_RUN_STATUSES.has(run.status))
      ),
    [processRuns]
  );
  const timelineItems = React.useMemo(() => {
    const messageItems = visibleMessages.map((message, index) => ({
      kind: 'message' as const,
      key: `${message.role}:${message.timestamp || index}:${index}`,
      sortTime: parseTimelineTime(message.timestamp) ?? Number.MAX_SAFE_INTEGER / 2 + index,
      order: index,
      message,
      messageIndex: index,
    }));
    const teamItems = teamGroups.map((group, index) => ({
        kind: 'team' as const,
        key: `team:${group.rootRun.run_id}`,
        sortTime: parseTimelineTime(group.startedAt) ?? Number.MAX_SAFE_INTEGER / 2 + visibleMessages.length + index,
        order: visibleMessages.length + index,
        group,
      }));

    return [...messageItems, ...teamItems].sort((a, b) => {
      if (a.sortTime !== b.sortTime) {
        return a.sortTime - b.sortTime;
      }
      return a.order - b.order;
    });
  }, [teamGroups, visibleMessages]);
  const taskCardMessageIndexes = React.useMemo(
    () => getTaskCardMessageIndexes(visibleMessages),
    [visibleMessages]
  );
  const latestFeedbackMessageIndex = (() => {
    for (let index = visibleMessages.length - 1; index >= 0; index -= 1) {
      const message = visibleMessages[index];
      if (
        message.role === 'assistant'
        && message.run_id
        && message.task_id
        && message.task_status === 'awaiting_acceptance'
        && hasRenderableMessageContent(message)
      ) {
        return index;
      }
    }
    return -1;
  })();

  return (
    <ScrollArea className="h-full px-3 sm:px-5 md:px-8" viewportRef={viewportRef}>
      <div className="mx-auto max-w-5xl space-y-8 py-6 md:py-10">
        {visibleMessages.length === 0 && teamGroups.length === 0 && !isThinking && (
          <div className="flex flex-col items-center justify-center py-20 text-muted-foreground">
            <Bot className="w-12 h-12 mb-4 opacity-50" />
            <p className="text-lg font-medium text-foreground">Beaver</p>
            <p className="text-sm">{pickAppText(locale, '发送消息开始对话', 'Send a message to start the conversation')}</p>
          </div>
        )}

        {timelineItems.map((item) =>
          item.kind === 'message' ? (
            <MessageBubble
              key={item.key}
              message={item.message}
              showTaskCard={taskCardMessageIndexes.has(item.messageIndex)}
              canSendFeedback={item.messageIndex === latestFeedbackMessageIndex}
              onFeedback={onFeedback}
              onRequestRevision={onRequestRevision}
            />
          ) : (
            <AgentTeamBlock
              key={item.key}
              rootRun={item.group.rootRun}
              memberRuns={item.group.memberRuns}
              events={processEvents}
              artifacts={processArtifacts}
              selectedRunId={selectedRunId}
              onSelectRun={onSelectRun}
            />
          )
        )}

        {isThinking && (
          <div className="flex items-center gap-2 text-muted-foreground px-1">
            <Bot className="w-5 h-5" />
            <Loader2 className="w-4 h-4 animate-spin" />
            <span className="text-sm">{pickAppText(locale, '思考中...', 'Thinking...')}</span>
          </div>
        )}

        <div ref={messagesEndRef} />
      </div>
    </ScrollArea>
  );
}
