'use client'

import * as React from 'react'
import Link from 'next/link'
import { ArrowLeft, Download, FileText, Palette, Presentation, Sparkles } from 'lucide-react'
import { useQuery } from '@tanstack/react-query'
import type { Attachment } from '@startsimpli/api'
import { DualPaneWorkspace } from '../workspace'
import { ChatThread, ChatComposer } from '../chat'
import type { ChatMessageData, ChatMessageAttachment } from '../chat/types'
import { SlideDeckViewer } from '../slide-deck'
import type { DeckBuilder } from './types'

/**
 * Reopen display: if no message already carries attachments (the conversation
 * payload didn't echo them), hang the document's persisted reference attachments
 * off the earliest user message so they reappear in the chat after reopen.
 */
function hydratePriorAttachments(
  messages: ChatMessageData[],
  prior: Attachment[] | undefined
): ChatMessageData[] {
  if (!prior || prior.length === 0) return messages
  if (messages.some((m) => m.attachments && m.attachments.length > 0)) return messages
  const targetIdx = messages.findIndex((m) => m.role === 'user')
  if (targetIdx === -1) return messages
  const chips: ChatMessageAttachment[] = prior
    .filter((a) => a.purpose === 'reference')
    .map((a) => ({
      id: a.id,
      fileUrl: a.fileUrl,
      filename: a.filename,
      contentType: a.contentType,
    }))
  if (chips.length === 0) return messages
  return messages.map((m, i) => (i === targetIdx ? { ...m, attachments: chips } : m))
}

export interface DeckBuilderWorkspaceProps {
  /** Where the back arrow points. Omit to hide it (e.g. on a public /try page). */
  backHref?: string
  /**
   * The orchestrator to render — injected, never imported here. The consuming
   * app wires a demo/no-backend engine or the backend workflow-run consumer;
   * both expose the identical `DeckBuilder` contract. Reference-attachment
   * support (uploader + reopen hydration) rides on `builder.attachments`.
   */
  builder: DeckBuilder
}

/**
 * The chat | slides deck-builder page composer. Composed entirely from generic
 * @startsimpli/ui primitives (`DualPaneWorkspace` + chat + `SlideDeckViewer`)
 * wired to an injected deck-builder orchestrator.
 *
 * Pure + app-agnostic: the orchestrator is a prop (`builder`) so the same
 * surface can be driven by a demo engine or a backend workflow-run consumer,
 * and the reference-attachments client rides on the builder — the app renders
 * this as a thin `<DeckBuilderWorkspace builder={...} />` wrapper.
 */
export function DeckBuilderWorkspace({ backHref, builder }: DeckBuilderWorkspaceProps) {
  const {
    deckTitle,
    phase,
    messages,
    slides,
    activeId,
    pptxUrl,
    pdfUrl,
    isWorking,
    documentId,
    deckStyle,
    attachments,
    setActiveId,
    sendMessage,
    regenerate,
    deleteSlide,
    moveSlide,
    insertSlide,
    editSlide,
    reorderSlide,
  } = builder

  // Only a backend-backed engine has a Document for reference uploads to land
  // on AND an attachments client wired up; a demo engine would strand every file
  // as a forever-queued chip, so it gets no uploader (attach affordance hidden).
  const canAttach = builder.supportsAttachments === true && !!attachments

  // A turn is in progress (agent thinking / slides generating). Prefer the
  // builder's own signal; fall back to slide status for engines that omit it.
  const busy = isWorking ?? slides.some((s) => s.status === 'generating')

  // Reference attachments are bundled into the shared ChatComposer: pass it the
  // document-scoped uploader and the composer self-manages upload state (chips,
  // downscale, retry, remove). Uploads are document-scoped — for a brand-new deck
  // with no document yet, the composer holds files client-side and flushes them
  // the moment the first turn creates the document and `documentId` arrives. The
  // backend agent gathers references off the Document, so we do NOT thread
  // attachment ids through the turn — landing the upload on `documentId` is enough.

  // Reopen: surface the document's persisted reference attachments. Fetched off
  // the injected attachments client (mirrors @startsimpli/hooks `useAttachments`
  // — the surface takes react-query as a peer, so keep the query inline rather
  // than couple this package to @startsimpli/hooks). Disabled until a real
  // documentId + client exist. Hydrated onto the earliest user message when the
  // conversation payload didn't already carry them, so they reappear on reopen.
  const { data: priorAttachments } = useQuery({
    queryKey: ['attachments', 'list', documentId ?? ''],
    queryFn: () => attachments!.listAttachments(documentId!),
    enabled: !!attachments && !!documentId && documentId !== 'new',
  })
  const messagesWithAttachments = React.useMemo(
    () => hydratePriorAttachments(messages, priorAttachments),
    [messages, priorAttachments]
  )

  // Compact display of the deck's derived style: theme + any non-"auto" palette/
  // font. "auto" is the placeholder default, so it's dropped to avoid noise.
  // Style values can be anything from a short label ("Modern Professional") to a
  // multi-sentence prose spec (when cloned from a reference image via vision), so
  // each bit is hard-truncated to a glanceable phrase and the chip is width-
  // capped + single-line — the full spec lives in the hover title.
  const shortBit = (v: string) => {
    const firstPhrase = v.split(/[.;,—]/)[0].trim()
    const s = (firstPhrase || v).trim()
    return s.length > 28 ? `${s.slice(0, 28).trimEnd()}…` : s
  }
  const rawBits = deckStyle
    ? [deckStyle.theme, deckStyle.palette, deckStyle.font]
        .filter((v): v is string => !!v && v !== 'auto')
        .filter((v, i, a) => a.indexOf(v) === i)
    : []
  const styleLabel = rawBits.map(shortBit).join(' · ')
  const styleTitle = [deckStyle?.theme, deckStyle?.palette, deckStyle?.font, deckStyle?.mood]
    .filter((v): v is string => !!v && v !== 'auto')
    .join('\n\n')
  const styleChip = deckStyle && (rawBits.length > 0 || deckStyle.source === 'reference') && (
    <div
      className="hidden max-w-[280px] items-center gap-1.5 rounded-md border border-input bg-background px-2.5 py-1.5 text-xs text-gray-600 sm:inline-flex"
      data-testid="deck-style-chip"
      title={styleTitle || 'Deck style'}
    >
      {deckStyle.source === 'reference' ? (
        <Sparkles className="h-3.5 w-3.5 shrink-0 text-primary" aria-hidden="true" />
      ) : (
        <Palette className="h-3.5 w-3.5 shrink-0 text-gray-400" aria-hidden="true" />
      )}
      <span className="truncate font-medium capitalize">{styleLabel || 'Custom style'}</span>
      {deckStyle.source === 'reference' && (
        <span className="shrink-0 text-primary">· cloned</span>
      )}
    </div>
  )

  const toolbar = (
    <div className="flex items-center justify-between gap-3 bg-white px-4 py-2">
      <div className="flex items-center gap-3">
        {backHref && (
          <Link
            href={backHref}
            className="rounded p-1.5 text-gray-400 hover:bg-gray-100 hover:text-gray-700"
            aria-label="Back"
          >
            <ArrowLeft className="h-4 w-4" />
          </Link>
        )}
        <Presentation className="h-5 w-5 text-primary" aria-hidden="true" />
        <div className="leading-tight">
          <div className="text-sm font-semibold text-gray-900">{deckTitle || 'New presentation'}</div>
          <div className="text-xs text-gray-400">
            {slides.length > 0 ? `${slides.length} slides · ${phase}` : 'Paste an outline to begin'}
          </div>
        </div>
      </div>
      <div className="flex items-center gap-2">
        {styleChip}
        {pptxUrl && (
          <a
            href={pptxUrl}
            download={`${deckTitle || 'deck'}.pptx.txt`}
            className="inline-flex items-center gap-1.5 rounded-md border border-input bg-background px-2.5 py-1.5 text-sm shadow-sm hover:bg-accent"
          >
            <Download className="h-3.5 w-3.5" /> PPTX
          </a>
        )}
        {pdfUrl && (
          <a
            href={pdfUrl}
            download={`${deckTitle || 'deck'}.pdf.txt`}
            className="inline-flex items-center gap-1.5 rounded-md border border-input bg-background px-2.5 py-1.5 text-sm shadow-sm hover:bg-accent"
          >
            <FileText className="h-3.5 w-3.5" /> PDF
          </a>
        )}
      </div>
    </div>
  )

  const chat = (
    <div className="flex h-full min-h-0 flex-col bg-white">
      <ChatThread
        messages={messagesWithAttachments}
        busy={busy}
        busyLabel="Working on your deck…"
        className="flex-1"
        emptyState={<span className="text-sm text-gray-400">Your conversation will appear here.</span>}
      />
      <ChatComposer
        onSubmit={sendMessage}
        busy={busy}
        uploader={canAttach ? attachments : undefined}
        documentId={documentId}
        placeholder={
          phase === 'empty'
            ? 'Paste your outline (title on line 1, then slides)…'
            : 'Reply — “looks good”, “redo slide 2 …”, “compile”…'
        }
      />
    </div>
  )

  const deck = (
    <SlideDeckViewer
      slides={slides}
      activeId={activeId}
      onActiveChange={setActiveId}
      onRegenerate={regenerate}
      onDelete={deleteSlide}
      onMove={moveSlide}
      onInsert={insertSlide}
      onEdit={editSlide}
      onReorder={reorderSlide}
      emptyState={
        <div className="max-w-sm">
          <Presentation className="mx-auto mb-3 h-10 w-10 text-gray-300" aria-hidden="true" />
          <p className="text-sm text-gray-500">
            Your slides will render here as a live deck — paste an outline in the chat to start.
          </p>
        </div>
      }
    />
  )

  return (
    <DualPaneWorkspace
      toolbar={toolbar}
      leftLabel="Chat"
      rightLabel="Slides"
      left={chat}
      right={deck}
      collapsible
      initialLeftSize={38}
      storageKey="present-web:builder-split"
      className="h-full"
    />
  )
}
