// @design-system: domain/chat — ThreadView
import { useEffect, useMemo, useState } from 'react';
import { useQuery } from '@tanstack/react-query';
import { formatTime } from '@/lib/format';
import { useLocale, useT } from '@/lib/i18n/react';
import { Card } from '@/components/ui/layout/Card';
import { EmptyState } from '@/components/ui/feedback/EmptyState';
import { ErrorState } from '@/components/ui/feedback/ErrorState';
import { ChatSkeleton, SkeletonGuard } from '@/components/ui/feedback/Skeleton';
import { Button } from '@/components/ui/primitives/Button';
import { useLiveNotifications, useLive } from '@/lib/hooks/use-live-notifications';
import type { ChatMessageRow } from '@/server/db/schema';
import { MessageComposer } from './MessageComposer';

interface Props {
  threadId: string;
  currentUserId: string;
  threadTitle?: string | null;
}

interface LivePayload {
  msgId: string;
  threadId: string;
  from: string;
  body: string;
  ts: string;
}

export function ThreadView({ threadId, currentUserId, threadTitle }: Props) {
  const t = useT('chat');
  const tCommon = useT('common');
  const { locale } = useLocale();
  const [liveMessages, setLiveMessages] = useState<ChatMessageRow[]>([]);
  const [optimisticMessages, setOptimisticMessages] = useState<ChatMessageRow[]>([]);
  const [seenIds] = useState(() => new Set<string>());

  useLiveNotifications();

  useLive(`chat:thread:${threadId}`, (frame) => {
    const f = frame as { event?: string; payload?: LivePayload };
    if (f.event !== 'chat.message' || !f.payload) return;
    const { msgId, from, body, ts } = f.payload;
    if (seenIds.has(msgId)) return;
    seenIds.add(msgId);
    setLiveMessages((prev) => [
      {
        id: msgId,
        threadId,
        senderUserId: from,
        body,
        createdAt: new Date(ts),
      } as ChatMessageRow,
      ...prev,
    ]);
  });

  const { data, isLoading, isError, refetch } = useQuery<{ items: ChatMessageRow[] }>({
    queryKey: ['chat', 'messages', threadId],
    queryFn: async () => {
      const res = await fetch(`/api/chat/threads/${threadId}/messages`, {
        credentials: 'same-origin',
      });
      const body = (await res.json()) as {
        ok: boolean;
        data?: { items: ChatMessageRow[] };
        error?: string;
      };
      if (!body.ok) throw new Error(body.error ?? 'load failed');
      return body.data!;
    },
  });

  useEffect(() => {
    if (data?.items) {
      for (const m of data.items) seenIds.add(m.id);
    }
  }, [data, seenIds]);

  const display = useMemo(() => {
    const fetched = data?.items ?? [];
    const merged = [
      ...optimisticMessages,
      ...liveMessages,
      ...fetched.filter((m) => !liveMessages.some((lm) => lm.id === m.id)),
    ];
    const unique = new Map<string, ChatMessageRow>();
    for (const m of merged) unique.set(m.id, m);
    return [...unique.values()].sort(
      (a, b) => Date.parse(String(a.createdAt)) - Date.parse(String(b.createdAt)),
    );
  }, [data?.items, liveMessages, optimisticMessages]);

  function handleMessageSent(message: ChatMessageRow) {
    seenIds.add(message.id);
    setOptimisticMessages((prev) => [message, ...prev.filter((m) => m.id !== message.id)]);
  }

  if (isLoading && !data) {
    return (
      <div aria-busy="true" role="status">
        <span className="sr-only">{t('loading')}</span>
        <SkeletonGuard>
          <ChatSkeleton variant="thread" count={6} />
        </SkeletonGuard>
      </div>
    );
  }

  if (isError) {
    return (
      <ErrorState
        title={tCommon('error_loading')}
        action={
          <Button variant="secondary" size="sm" onClick={() => refetch()}>
            {tCommon('retry')}
          </Button>
        }
      />
    );
  }

  return (
    <div data-skeleton-ready className="flex h-full flex-col gap-[var(--space-3)]">
      <header className="flex flex-col gap-1 border-b border-[var(--color-border)] pb-[var(--space-2)]">
        <a
          href="/threads"
          className="text-text-secondary hover:text-text-primary text-sm font-medium underline-offset-2 hover:underline"
        >
          {t('back_to_threads')}
        </a>
        <h2 className="text-text-primary text-lg font-semibold">{threadTitle ?? t('untitled')}</h2>
      </header>

      {display.length === 0 ? (
        <EmptyState title={t('empty_thread_title')} description={t('empty_thread_body')} />
      ) : (
        <ul className="flex flex-1 flex-col-reverse gap-[var(--space-2)] overflow-y-auto p-[var(--space-2)]">
          {display.map((m) => {
            const isOwn = m.senderUserId === currentUserId;
            const senderLabel = isOwn ? t('sender_you') : t('sender_other');
            return (
              <li key={m.id} className="flex">
                <Card
                  padding="sm"
                  aria-label={m.body}
                  className={
                    isOwn
                      ? 'ms-auto max-w-[70%] bg-[var(--color-accent-bg)]'
                      : 'me-auto max-w-[70%]'
                  }
                >
                  <span className="sr-only">{senderLabel}</span>
                  <p className="text-sm break-words">{m.body}</p>
                  <time className="mt-[var(--space-1)] block text-xs text-[var(--color-text-muted)]">
                    {formatTime(m.createdAt, locale)}
                  </time>
                </Card>
              </li>
            );
          })}
        </ul>
      )}

      <MessageComposer threadId={threadId} onMessageSent={handleMessageSent} />
    </div>
  );
}
