import { useState } from 'react';
import { useTranslation } from 'react-i18next';
import { LocalTaskComment, getLocalFileRawUrl } from '../../api';
import { MarkdownPreview } from '../files/FilePreview';
import { MarkdownText } from '../../lib/markdown-text';
import { ToolCallsSection, parseToolCallComment } from './ToolCallsSection';

// We delegate spacing to MarkdownText's built-in COMPACT_PROSE default
// (4px-ish paragraph margins, leading-snug, `!important` to beat the
// prose plugin). Don't pass a className here — that would replace
// the compact preset with whatever string we pass, which is exactly
// the bug we kept reintroducing earlier.

interface ChatMessageProps {
  comment: LocalTaskComment;
  taskId: number;
  /** Override the base URL used for inline Markdown images (e.g. for shared view) */
  imageBaseUrl?: string;
  /** When true, this thinking comment has been superseded — show static dot instead of spinner */
  isStaleThinking?: boolean;
}

interface ProgressData {
  movement: string;
  tools: Record<string, number>;
  durationMs: number;
}

interface ThinkingData {
  type: 'thinking';
  text: string;
  movement?: string;
}

function tryParseInterjectionAck(body: string): { commentIds: number[]; movement: string } | null {
  try {
    const data = JSON.parse(body);
    if (data && data.type === 'interjection_ack' && Array.isArray(data.commentIds)) {
      return data;
    }
  } catch { /* not ack JSON */ }
  return null;
}

function tryParseThinking(body: string): ThinkingData | null {
  try {
    const data = JSON.parse(body);
    if (data && data.type === 'thinking' && typeof data.text === 'string') {
      return data as ThinkingData;
    }
  } catch { /* not thinking JSON */ }
  return null;
}

interface ChecklistProgressData {
  type: 'checklist';
  name: string;
  items: Array<{
    id: string;
    label: string;
    status: 'pending' | 'done' | 'failed' | 'skipped';
    result: string | null;
    error: string | null;
  }>;
  summary: {
    total: number;
    done: number;
    failed: number;
    skipped: number;
    remaining: number;
  };
}

function tryParseChecklistProgress(body: string): ChecklistProgressData | null {
  try {
    const data = JSON.parse(body);
    if (data && data.type === 'checklist' && data.name && data.items && data.summary) {
      return data as ChecklistProgressData;
    }
  } catch { /* not checklist JSON */ }
  return null;
}

function tryParseProgress(body: string): ProgressData | null {
  try {
    const data = JSON.parse(body);
    if (
      data &&
      typeof data.movement === 'string' &&
      typeof data.durationMs === 'number' &&
      data.tools && typeof data.tools === 'object' &&
      data.type !== 'tool_call'
    ) {
      return data as ProgressData;
    }
  } catch { /* not JSON */ }
  return null;
}

function formatDuration(ms: number): string {
  if (ms < 1000) return `${ms}ms`;
  const sec = Math.round(ms / 1000);
  if (sec < 60) return `${sec}s`;
  const min = Math.floor(sec / 60);
  return `${min}m ${sec % 60}s`;
}

function ChecklistCard({ comment }: { comment: LocalTaskComment }) {
  const { t } = useTranslation('chat');
  const [expanded, setExpanded] = useState(false);
  const data = tryParseChecklistProgress(comment.body);
  if (!data) return null;

  const { name, items, summary } = data;
  const pct = summary.total > 0 ? Math.round(((summary.done + summary.failed + summary.skipped) / summary.total) * 100) : 0;

  // Show max 20 items in collapsed, all in expanded
  const displayItems = expanded ? items : items.slice(0, 20);
  const hasMore = !expanded && items.length > 20;

  // Refero refresh: replace unicode glyphs with inline SVG so icons render
  // consistently across font stacks and align with the new palette.
  const StatusIcon = ({ status }: { status: string }) => {
    const common = 'inline-block w-3.5 h-3.5 flex-shrink-0';
    switch (status) {
      case 'done':
        return (
          <svg className={`${common} text-emerald-600`} viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
            <rect x="2" y="2" width="12" height="12" rx="2.5" fill="currentColor" fillOpacity="0.12" stroke="currentColor" />
            <path d="M5 8.5l2 2 4-4.5" />
          </svg>
        );
      case 'failed':
        return (
          <svg className={`${common} text-red-600`} viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
            <rect x="2" y="2" width="12" height="12" rx="2.5" fill="currentColor" fillOpacity="0.1" stroke="currentColor" />
            <path d="M5.5 5.5l5 5M10.5 5.5l-5 5" />
          </svg>
        );
      case 'skipped':
        return (
          <svg className={`${common} text-slate-400`} viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.75" strokeLinecap="round">
            <rect x="2" y="2" width="12" height="12" rx="2.5" stroke="currentColor" />
            <path d="M4.5 4.5l7 7" />
          </svg>
        );
      default:
        return (
          <svg className={`${common} text-slate-300`} viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.5">
            <rect x="2" y="2" width="12" height="12" rx="2.5" />
          </svg>
        );
    }
  };

  return (
    <div className="flex justify-center">
      <div className="bg-canvas border border-hairline rounded-md px-3.5 py-2.5 max-w-[90%] w-full">
        {/* Header */}
        <button
          onClick={() => setExpanded(!expanded)}
          className="w-full flex items-center justify-between text-left hover:bg-surface rounded -mx-1 px-1 py-0.5 transition-colors"
        >
          <div className="flex items-center gap-2 min-w-0">
            <svg className="w-4 h-4 text-slate-500 flex-shrink-0" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.75" strokeLinecap="round" strokeLinejoin="round">
              <path d="M9 11l3 3L22 4" />
              <path d="M21 12v7a2 2 0 01-2 2H5a2 2 0 01-2-2V5a2 2 0 012-2h11" />
            </svg>
            <span className="text-[13px] font-semibold text-slate-900 truncate">{name}</span>
          </div>
          <div className="flex items-center gap-2 flex-shrink-0">
            <span className="text-2xs text-slate-500 font-mono tabular-nums">
              {summary.done + summary.failed + summary.skipped}/{summary.total} {'\u00B7'} {pct}%
            </span>
            <svg className={`w-3 h-3 text-slate-400 transition-transform ${expanded ? 'rotate-90' : ''}`} viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
              <path d="M6 4l4 4-4 4" />
            </svg>
          </div>
        </button>

        {/* Summary badges */}
        <div className="flex gap-1.5 mt-2 text-2xs">
          {summary.done > 0 && (
            <span className="inline-flex items-center gap-1 bg-emerald-50 dark:bg-emerald-500/15 text-emerald-700 dark:text-emerald-300 border border-emerald-100 dark:border-emerald-500/30 px-1.5 py-0.5 rounded font-mono tabular-nums">
              <StatusIcon status="done" />{summary.done}
            </span>
          )}
          {summary.failed > 0 && (
            <span className="inline-flex items-center gap-1 bg-red-50 dark:bg-red-500/15 text-red-700 dark:text-red-300 border border-red-100 dark:border-red-500/30 px-1.5 py-0.5 rounded font-mono tabular-nums">
              <StatusIcon status="failed" />{summary.failed}
            </span>
          )}
          {summary.skipped > 0 && (
            <span className="inline-flex items-center gap-1 bg-surface-2 text-slate-600 border border-hairline px-1.5 py-0.5 rounded font-mono tabular-nums">
              <StatusIcon status="skipped" />{summary.skipped}
            </span>
          )}
          {summary.remaining > 0 && (
            <span className="inline-flex items-center gap-1 bg-canvas text-slate-500 border border-hairline px-1.5 py-0.5 rounded font-mono tabular-nums">
              <StatusIcon status="pending" />{summary.remaining}
            </span>
          )}
        </div>

        {/* Items list */}
        {(expanded || items.length <= 20) && (
          <div className="mt-2 pt-2 border-t border-hairline-soft max-h-[400px] overflow-y-auto">
            {displayItems.map(item => (
              <div key={item.id} className="flex items-start gap-2 py-1 text-xs">
                <span className="mt-0.5"><StatusIcon status={item.status} /></span>
                <span className="min-w-0 flex-1">
                  <span className="block text-slate-700 truncate" title={item.label || item.id}>
                    {item.label || item.id}
                  </span>
                  {item.label && item.label !== item.id && (
                    <span className="block text-[10px] leading-tight text-slate-400 font-mono truncate" title={item.id}>
                      {item.id}
                    </span>
                  )}
                </span>
                {item.status === 'done' && item.result && (
                  <span className="text-slate-400 truncate max-w-[200px] font-mono text-2xs">{'\u2192'} {item.result}</span>
                )}
                {item.status === 'failed' && item.error && (
                  <span className="text-red-500 truncate max-w-[200px] font-mono text-2xs">{item.error}</span>
                )}
              </div>
            ))}
            {hasMore && (
              <button
                onClick={() => setExpanded(true)}
                className="text-2xs text-slate-500 hover:text-slate-900 hover:underline mt-1.5"
              >
                {t('checklist.showMore', { count: items.length - 20 })}
              </button>
            )}
          </div>
        )}
      </div>
    </div>
  );
}

function Spinner() {
  return (
    <svg className="w-3 h-3 animate-spin text-slate-500" viewBox="0 0 24 24" fill="none" aria-hidden="true">
      <circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4" />
      <path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8v4a4 4 0 00-4 4H4z" />
    </svg>
  );
}

function ProgressPill({ icon, children, variant = 'inline' }: { icon: React.ReactNode; children: React.ReactNode; variant?: 'inline' | 'block' }) {
  // inline: short one-liner (movement summary / fallback) — pill shape
  // block: potentially multi-line thinking text — rounded rectangle to avoid weird ellipse corners
  const shapeCls = variant === 'block'
    ? 'rounded-xl rounded-bl-md px-3 py-2 items-start max-w-[85%]'
    : 'rounded-full px-3 py-1.5 items-center max-w-[90%]';
  // Use a div for the text wrapper so children can be block-level MD output
  // (MarkdownText renders a <div>). NB: NO whitespace-pre-wrap here — that
  // turns the literal `\n` between `</p>` and `<ol>` (etc.) in the marked
  // output into a rendered newline, adding ~22px of empty vertical space
  // between every Markdown block. The plain-text callsites in this file
  // (movement summary string) are single-line, so they don't need it
  // either.
  return (
    <div className="flex justify-start">
      <div className={`inline-flex gap-2 bg-slate-50 border border-slate-200 text-xs text-slate-600 ${shapeCls}`}>
        <span className={`flex-shrink-0 ${variant === 'block' ? 'mt-0.5' : ''}`}>{icon}</span>
        <div className="break-words min-w-0">{children}</div>
      </div>
    </div>
  );
}

function ProgressCard({ comment, isStaleThinking }: { comment: LocalTaskComment; isStaleThinking?: boolean }) {
  const { t } = useTranslation('chat');
  // Interjection ack → minimal centered confirmation
  const ackData = tryParseInterjectionAck(comment.body);
  if (ackData) {
    return (
      <div className="flex justify-center">
        <div className="inline-flex items-center gap-1.5 px-3 py-1 text-[10px] text-green-600 font-medium">
          <span>{'✓'}</span>
          <span>{t('message.messageAcked')}</span>
        </div>
      </div>
    );
  }

  // Tool call comment → render as single-item ToolCallsSection.
  // This path fires when a tool_call comment is emitted before the parent
  // movement-complete arrives (live tool calls during running movement).
  const toolCall = parseToolCallComment(comment.body);
  if (toolCall) {
    return <ToolCallsSection toolCalls={[toolCall]} />;
  }

  // Checklist progress → dedicated card (center, retained as per decision)
  const checklistData = tryParseChecklistProgress(comment.body);
  if (checklistData) {
    return <ChecklistCard comment={comment} />;
  }

  // Thinking / in-flight LLM text \u2014 MD render so streaming output that
  // contains lists / fenced code / inline backticks looks right.
  const thinking = tryParseThinking(comment.body);
  if (thinking) {
    const icon = isStaleThinking
      ? <span className="text-slate-400">{'\u2026'}</span>
      : <Spinner />;
    return (
      <ProgressPill icon={icon} variant="block">
        <MarkdownText text={thinking.text} />
      </ProgressPill>
    );
  }

  // Movement completion summary JSON \u2014 structured one-liner, keep plain.
  const data = tryParseProgress(comment.body);
  if (data) {
    const toolEntries = Object.entries(data.tools);
    const toolSummary = toolEntries.map(([name, count]) => `${name}\u00D7${count}`).join(', ');
    const text = `${t('movement.complete', { movement: data.movement })}${toolSummary ? ` \u00B7 ${toolSummary}` : ''} \u00B7 ${formatDuration(data.durationMs)}`;
    return <ProgressPill icon={<span className="text-green-600">{'\u2713'}</span>}>{text}</ProgressPill>;
  }

  // Fallback: free-form text, MD render
  return (
    <ProgressPill icon={<span className="text-slate-400">{'\u2022'}</span>}>
      <MarkdownText text={comment.body} />
    </ProgressPill>
  );
}

/**
 * Download chips for files the user attached to a comment. Files live under the
 * task's input/ dir; we link to the raw-file endpoint with a download attribute.
 */
function CommentAttachments({ attachments, taskId }: { attachments?: string[]; taskId: number }) {
  if (!attachments || attachments.length === 0) return null;
  return (
    <div className="mt-2 flex flex-wrap gap-1.5">
      {attachments.map(name => (
        <a
          key={name}
          href={getLocalFileRawUrl(taskId, 'input', name)}
          download={name}
          target="_blank"
          rel="noopener noreferrer"
          title={name}
          className="inline-flex items-center gap-1 max-w-[200px] px-2 py-1 rounded-md bg-white/70 dark:bg-slate-900/40 border border-slate-200 dark:border-slate-600 text-2xs text-slate-700 dark:text-slate-200 hover:bg-white dark:hover:bg-slate-800 hover:border-accent transition-colors"
        >
          <svg className="w-3 h-3 shrink-0 text-slate-400" viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round">
            <path d="M10 2.5 4.2 8.3a2 2 0 0 0 2.8 2.8l5.3-5.3a3.2 3.2 0 0 0-4.5-4.5L2.4 7.2" />
          </svg>
          <span className="truncate">{name}</span>
        </a>
      ))}
    </div>
  );
}

export function ChatMessage({ comment, taskId, imageBaseUrl, isStaleThinking }: ChatMessageProps) {
  const { t } = useTranslation('chat');
  const { kind, author, body, createdAt } = comment;

  // Progress card (center)
  if (kind === 'progress') {
    return <ProgressCard comment={comment} isStaleThinking={isStaleThinking} />;
  }

  // Interjection (user message sent during running)
  if (kind === 'interjection') {
    const isPending = !comment.injectedAt;
    return (
      <div className="flex justify-end">
        <div className="max-w-[82%] bg-amber-50 dark:bg-amber-500/15 border border-amber-200 dark:border-amber-500/30 text-slate-900 rounded-2xl rounded-br-md px-4 py-3">
          <div className="text-2xs text-amber-500 mb-1.5">
            {author} · {new Date(createdAt).toLocaleString()}
          </div>
          <MarkdownText text={body} />
          <CommentAttachments attachments={comment.attachments} taskId={taskId} />
          <div className={`text-[10px] mt-1.5 ${isPending ? 'text-amber-400' : 'text-green-500'}`}>
            {isPending ? t('message.waitingAgentAck') : t('message.acked', { time: new Date(comment.injectedAt!).toLocaleTimeString() })}
          </div>
        </div>
      </div>
    );
  }

  // User messages (right, soft slate — kit style)
  if (kind === 'request' || kind === 'comment') {
    return (
      <div className="flex justify-end">
        <div className="max-w-[82%] bg-slate-100 text-slate-900 rounded-2xl rounded-br-md px-4 py-3">
          <div className="text-2xs text-slate-400 mb-1.5">
            {author} · {new Date(createdAt).toLocaleString()}
          </div>
          <MarkdownText text={body} />
          <CommentAttachments attachments={comment.attachments} taskId={taskId} />
        </div>
      </div>
    );
  }

  // Agent ask (left, yellow)
  if (kind === 'ask') {
    return (
      <div className="flex justify-start">
        <div className="max-w-[82%] bg-amber-50 dark:bg-amber-500/15 border border-amber-200 dark:border-amber-500/30 text-slate-900 rounded-2xl rounded-bl-md px-4 py-3 shadow-sm">
          <div className="text-2xs text-amber-500 mb-1.5">
            {author} · {new Date(createdAt).toLocaleString()}
          </div>
          <MarkdownText text={body} />
        </div>
      </div>
    );
  }

  // Agent result (left, green) - render with Markdown
  if (kind === 'result') {
    return (
      <div className="flex justify-start">
        <div className="w-full bg-green-50 dark:bg-green-500/15 border border-green-200 dark:border-green-500/30 text-slate-900 rounded-xl px-4 py-3 shadow-sm">
          <div className="text-2xs text-green-500 mb-1.5">
            {author} · {new Date(createdAt).toLocaleString()}
          </div>
          <div className="text-sm leading-relaxed prose prose-sm prose-slate dark:prose-invert max-w-none">
            <MarkdownPreview content={body} imageBaseUrl={imageBaseUrl ?? `/api/local/tasks/${taskId}/files/raw?section=output&path=`} taskId={taskId} />
          </div>
        </div>
      </div>
    );
  }

  // Fallback for unknown kinds
  return (
    <div className="flex justify-start">
      <div className="max-w-[82%] bg-canvas border border-slate-200 text-slate-900 rounded-2xl rounded-bl-md px-4 py-3 shadow-sm">
        <div className="text-2xs text-slate-400 mb-1.5">
          {author} · {new Date(createdAt).toLocaleString()}
        </div>
        <MarkdownText text={body} />
      </div>
    </div>
  );
}
