import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
import { useParams, useNavigate, Link } from 'react-router-dom'
import { useState, useMemo, Fragment } from 'react'
import {
  ArrowLeft, Send, Trash2,
  FileBox, AlertTriangle, CheckCircle2, Image as ImageIcon, Unlink,
  RotateCcw, LayoutList, LayoutGrid, X,
  ChevronDown, ChevronUp, ChevronsUpDown,
  Search, SlidersHorizontal, FileSpreadsheet, Box,
  Loader2, Play, RefreshCw, ExternalLink, Ban, StopCircle, Scissors, Plus, Wand2, Download,
} from 'lucide-react'
import { toast } from 'sonner'
import { getOrder, submitOrder, deleteOrder, unlinkCadFile, regenerateItemThumbnail, patchOrderItem, removeOrderLine, dispatchRenders, cancelLineRender, cancelOrderRenders, splitMissingStep, generateLinesFromItems, downloadOrderRenders } from '../api/orders'
import type { OrderItem, OrderLine } from '../api/orders'
import { listOutputTypes } from '../api/outputTypes'
import type { OutputType } from '../api/outputTypes'
import { useAuthStore } from '../store/auth'
import StepDropzone from '../components/upload/StepDropzone'
import CadPartMaterials from '../components/orders/CadPartMaterials'
import LiveRenderLog from '../components/LiveRenderLog'

// ── Filter / sort types ───────────────────────────────────────────────────────

type SortKey = 'row_index' | 'name_cad_modell' | 'baureihe' | 'item_status'
type SortDir = 'asc' | 'desc'

interface TableFilters {
  search: string
  itemStatus: '' | 'pending' | 'approved' | 'rejected'
  stepStatus: '' | 'with_step' | 'missing_step'
  rendering: '' | 'rendering' | 'non_rendering'
}

const EMPTY_FILTERS: TableFilters = {
  search: '',
  itemStatus: '',
  stepStatus: '',
  rendering: '',
}

function countActiveFilters(f: TableFilters): number {
  return (
    (f.search.trim() !== '' ? 1 : 0) +
    (f.itemStatus !== '' ? 1 : 0) +
    (f.stepStatus !== '' ? 1 : 0) +
    (f.rendering !== '' ? 1 : 0)
  )
}

// ── Page ─────────────────────────────────────────────────────────────────────

export default function OrderDetailPage() {
  const { id } = useParams<{ id: string }>()
  const navigate = useNavigate()
  const qc = useQueryClient()
  const user = useAuthStore((s) => s.user)

  const [galleryView, setGalleryView] = useState(false)
  const [gallerySelected, setGallerySelected] = useState<any | null>(null)
  const [genLinesOpen, setGenLinesOpen] = useState(false)
  const [genLinesSelected, setGenLinesSelected] = useState<Record<string, boolean>>({})
  const [isDownloading, setIsDownloading] = useState(false)

  // Table state
  const [filters, setFilters] = useState<TableFilters>(EMPTY_FILTERS)
  const [sortKey, setSortKey] = useState<SortKey>('row_index')
  const [sortDir, setSortDir] = useState<SortDir>('asc')
  const [expandedId, setExpandedId] = useState<string | null>(null)

  const { data: order, isLoading } = useQuery({
    queryKey: ['order', id],
    queryFn: () => getOrder(id!),
  })

  const submitMut = useMutation({
    mutationFn: () => submitOrder(id!),
    onSuccess: () => {
      toast.success('Order submitted')
      qc.invalidateQueries({ queryKey: ['order', id] })
    },
    onError: (e: any) => toast.error(e.response?.data?.detail || 'Submit failed'),
  })

  const deleteMut = useMutation({
    mutationFn: () => deleteOrder(id!),
    onSuccess: () => {
      toast.success('Order deleted')
      navigate('/orders')
    },
    onError: (e: any) => toast.error(e.response?.data?.detail || 'Delete failed'),
  })

  const dispatchMut = useMutation({
    mutationFn: () => dispatchRenders(id!),
    onSuccess: (data) => {
      toast.success(`${data.dispatched} render${data.dispatched !== 1 ? 's' : ''} dispatched`)
      qc.invalidateQueries({ queryKey: ['order', id] })
    },
    onError: (e: any) => toast.error(e.response?.data?.detail || 'Dispatch failed'),
  })

  const cancelAllMut = useMutation({
    mutationFn: () => cancelOrderRenders(id!),
    onSuccess: (data) => {
      toast.success(`${data.cancelled} render${data.cancelled !== 1 ? 's' : ''} cancelled`)
      if (data.errors?.length) toast.warning(`Some cancellations had issues: ${data.errors.join('; ')}`)
      qc.invalidateQueries({ queryKey: ['order', id] })
    },
    onError: (e: any) => toast.error(e.response?.data?.detail || 'Cancel failed'),
  })

  const { data: allOutputTypes = [] } = useQuery<OutputType[]>({
    queryKey: ['output-types'],
    queryFn: () => listOutputTypes(false),
    enabled: genLinesOpen,
  })

  const generateLinesMut = useMutation({
    mutationFn: () => {
      const typeIds = Object.entries(genLinesSelected).filter(([, v]) => v).map(([k]) => k)
      return generateLinesFromItems(id!, typeIds)
    },
    onSuccess: (data) => {
      toast.success(`${data.created} line${data.created !== 1 ? 's' : ''} created`)
      if (data.no_product_count > 0) {
        toast.warning(`${data.no_product_count} item${data.no_product_count !== 1 ? 's' : ''} could not be matched to a product`)
      }
      if (data.no_step_count > 0) {
        toast.warning(`${data.no_step_count} product${data.no_step_count !== 1 ? 's' : ''} have no STEP file — upload STEP files before dispatching renders`)
      }
      setGenLinesOpen(false)
      setGenLinesSelected({})
      qc.invalidateQueries({ queryKey: ['order', id] })
    },
    onError: (e: any) => toast.error(e.response?.data?.detail || 'Failed to generate lines'),
  })

  const splitMut = useMutation({
    mutationFn: () => splitMissingStep(id!),
    onSuccess: (data) => {
      const total = data.moved_item_count + data.moved_line_count
      toast.success(
        `Draft order ${data.new_order_number} created — ${total} item${total !== 1 ? 's' : ''} without STEP moved there`,
      )
      qc.invalidateQueries({ queryKey: ['order', id] })
      qc.invalidateQueries({ queryKey: ['orders'] })
    },
    onError: (e: any) => toast.error(e.response?.data?.detail || 'Split failed'),
  })

  if (isLoading) return <div className="p-8 text-center text-content-muted">Loading…</div>
  if (!order) return <div className="p-8 text-center text-red-500">Order not found</div>

  const canSubmit = order.status === 'draft'
  const canDelete = order.status === 'draft' || order.status === 'rejected'
  const isDraft = order.status === 'draft'
  const isPrivileged = user?.role === 'admin' || user?.role === 'project_manager'
  const rp = order.render_progress
  const hasRetryable = rp && (rp.pending > 0 || rp.failed > 0 || (rp as any).cancelled > 0)
  const canDispatch = isPrivileged && (order.status === 'processing' || order.status === 'submitted' || order.status === 'completed')
  const hasActiveRenders = rp && (rp.processing > 0 || rp.pending > 0)
  const canCancelRenders = isPrivileged && order.status === 'processing' && hasActiveRenders
  const hasCompletedRenders = rp && rp.completed > 0

  async function handleDownloadRenders() {
    setIsDownloading(true)
    try {
      await downloadOrderRenders(id!, order!.order_number)
    } catch (e: any) {
      toast.error(e.response?.data?.detail || 'Download failed')
    } finally {
      setIsDownloading(false)
    }
  }

  // Items marked for rendering require a STEP file before submission
  const renderingItems = order.items.filter((i: any) => i.medias_rendering)
  const missingStep = renderingItems.filter((i: any) => !i.cad_file_id)
  // Order lines with an output type also require the product to have a STEP file
  const missingStepLines = (order.lines ?? []).filter(
    (l: any) => l.output_type_id && !l.product?.cad_file_id,
  )
  const allRenderingHaveStep = missingStep.length === 0 && missingStepLines.length === 0
  const totalMissingStep = missingStep.length + missingStepLines.length
  // Allow admin/PM to generate lines for orders that have items but no lines
  const canGenerateLines = isPrivileged
    && (isDraft || order.status === 'submitted')
    && (order.lines?.length ?? 0) === 0
    && (order.items?.length ?? 0) > 0

  function handleSort(key: SortKey) {
    if (sortKey === key) {
      setSortDir((d) => (d === 'asc' ? 'desc' : 'asc'))
    } else {
      setSortKey(key)
      setSortDir('asc')
    }
  }

  function updateFilter<K extends keyof TableFilters>(key: K, value: TableFilters[K]) {
    setFilters((prev) => ({ ...prev, [key]: value }))
  }

  const activeFilterCount = countActiveFilters(filters)

  return (
    <div className="p-8">
      {/* Header */}
      <div className="flex items-center gap-4 mb-6 flex-wrap">
        <Link to="/orders" className="btn-secondary">
          <ArrowLeft size={16} />
          Back
        </Link>
        <div className="flex-1 min-w-0">
          <h1 className="text-2xl font-bold text-content">{order.order_number}</h1>
          <p className="text-sm text-content-secondary mt-0.5">
            {order.items.length} items · Created {new Date(order.created_at).toLocaleString('de-DE')}
          </p>
        </div>
        <StatusBadge status={order.status} />
        {order.estimated_price != null && (
          <div className="text-sm text-content-secondary">
            Estimated: <span className="font-semibold">{Number(order.estimated_price).toFixed(2)}</span>
          </div>
        )}
        {canDelete && (
          <button
            onClick={() => { if (confirm('Delete this order?')) deleteMut.mutate() }}
            className="btn-danger"
            disabled={deleteMut.isPending}
          >
            <Trash2 size={16} />
            Delete
          </button>
        )}
        {canCancelRenders && (
          <button
            onClick={() => {
              if (confirm(`Cancel all ${rp!.processing + rp!.pending} active render(s)?`))
                cancelAllMut.mutate()
            }}
            className="px-3 py-2 rounded-lg text-sm font-medium border border-border-default text-status-warning-text bg-status-warning-bg hover:bg-surface-hover transition-colors disabled:opacity-50 flex items-center gap-1.5"
            disabled={cancelAllMut.isPending}
          >
            <StopCircle size={16} />
            {cancelAllMut.isPending ? 'Cancelling…' : 'Cancel Renders'}
          </button>
        )}
        {hasCompletedRenders && (
          <button
            onClick={handleDownloadRenders}
            className="px-3 py-2 rounded-lg text-sm font-medium border border-border-default text-status-info-text bg-status-info-bg hover:bg-surface-hover transition-colors disabled:opacity-50 flex items-center gap-1.5"
            disabled={isDownloading}
          >
            <Download size={16} />
            {isDownloading ? 'Downloading…' : `Download Renders (${rp!.completed})`}
          </button>
        )}
        {canDispatch && (
          <button
            onClick={() => dispatchMut.mutate()}
            className="btn-secondary"
            disabled={dispatchMut.isPending}
          >
            {order.status === 'completed' ? <RefreshCw size={16} /> : rp && rp.failed > 0 ? <RefreshCw size={16} /> : <Play size={16} />}
            {dispatchMut.isPending
              ? 'Dispatching…'
              : order.status === 'completed'
                ? 'Re-submit Renders'
                : rp && rp.failed > 0
                  ? 'Retry Failed'
                  : 'Dispatch Renders'}
          </button>
        )}
        {canSubmit && (
          <button
            onClick={() => submitMut.mutate()}
            className="btn-primary"
            disabled={submitMut.isPending || !allRenderingHaveStep}
            title={!allRenderingHaveStep ? `${totalMissingStep} item${totalMissingStep !== 1 ? 's' : ''} still need a STEP file` : undefined}
          >
            <Send size={16} />
            {submitMut.isPending ? 'Submitting…' : 'Submit Order'}
          </button>
        )}
      </div>

      {/* Draft action banner */}
      {isDraft && (
        <div className="card p-4 mb-6 bg-accent-light border-accent">
          {allRenderingHaveStep ? (
            <div className="flex items-center gap-4">
              <div className="flex-1">
                <p className="text-sm font-semibold text-content">
                  This order is a draft — all rendering items have STEP files attached.
                </p>
                <p className="text-xs text-content-secondary mt-0.5">Ready to submit.</p>
              </div>
              <button
                onClick={() => submitMut.mutate()}
                className="px-5 py-2.5 rounded-lg text-sm font-semibold bg-accent text-white hover:bg-accent-hover transition-colors disabled:opacity-40 disabled:cursor-not-allowed flex items-center gap-2"
                disabled={submitMut.isPending}
              >
                <Send size={16} />
                {submitMut.isPending ? 'Submitting…' : 'Submit Order'}
              </button>
            </div>
          ) : (
            <div className="space-y-3">
              <div className="flex items-start gap-2">
                <AlertTriangle size={16} className="text-amber-500 mt-0.5 shrink-0" />
                <div>
                  <p className="text-sm font-semibold text-content">
                    {totalMissingStep} item{totalMissingStep !== 1 ? 's' : ''} still need a STEP file
                  </p>
                  <p className="text-xs text-content-secondary mt-0.5">
                    Upload the missing STEP files below, or move these items to a separate draft order so you can submit the rest now.
                  </p>
                </div>
              </div>
              <div className="flex items-center gap-3 flex-wrap">
                <button
                  onClick={() => {
                    if (confirm(`Move ${totalMissingStep} item${totalMissingStep !== 1 ? 's' : ''} without STEP to a new draft order and submit this one?`))
                      splitMut.mutate()
                  }}
                  className="flex items-center gap-2 px-4 py-2 rounded-lg text-sm font-medium border border-border-default text-status-warning-text bg-status-warning-bg hover:bg-surface-hover transition-colors disabled:opacity-50"
                  disabled={splitMut.isPending}
                >
                  <Scissors size={15} />
                  {splitMut.isPending ? 'Moving…' : `Move ${totalMissingStep} item${totalMissingStep !== 1 ? 's' : ''} without STEP to draft`}
                </button>
                <button
                  onClick={() => submitMut.mutate()}
                  className="px-5 py-2.5 rounded-lg text-sm font-semibold bg-accent text-white hover:bg-accent-hover transition-colors disabled:opacity-40 disabled:cursor-not-allowed flex items-center gap-2"
                  disabled={submitMut.isPending || !allRenderingHaveStep}
                  title={`${totalMissingStep} item${totalMissingStep !== 1 ? 's' : ''} still need a STEP file`}
                >
                  <Send size={16} />
                  {submitMut.isPending ? 'Submitting…' : 'Submit Order'}
                </button>
              </div>
            </div>
          )}
        </div>
      )}

      {/* Missing STEP file warning — only for rendering items */}
      {isDraft && !allRenderingHaveStep && (
        <div className="card p-4 mb-6 bg-status-warning-bg border-border-default flex items-start gap-3">
          <AlertTriangle size={18} className="text-amber-500 shrink-0 mt-0.5" />
          <div>
            <p className="text-sm font-semibold text-status-warning-text">
              {missingStep.length} rendering item{missingStep.length > 1 ? 's' : ''} still need a STEP file
            </p>
            <p className="text-xs text-status-warning-text mt-0.5">
              Upload the corresponding .stp files below — matched automatically by filename.
              Submission is blocked until all items marked for rendering have a CAD file.
            </p>
            <div className="mt-1 flex flex-wrap gap-1">
              {missingStep.slice(0, 8).map((item: any) => (
                <span key={item.id} className="text-xs font-mono bg-status-warning-bg text-status-warning-text px-1.5 py-0.5 rounded">
                  {item.name_cad_modell || `row ${item.row_index}`}
                </span>
              ))}
              {missingStep.length > 8 && (
                <span className="text-xs text-amber-600">+{missingStep.length - 8} more</span>
              )}
            </div>
          </div>
        </div>
      )}

      {/* All rendering items have STEP files */}
      {isDraft && allRenderingHaveStep && renderingItems.length > 0 && (
        <div className="card p-4 mb-6 bg-status-success-bg border-border-default flex items-center gap-3">
          <CheckCircle2 size={18} className="text-green-500 shrink-0" />
          <p className="text-sm text-status-success-text font-medium">
            All {renderingItems.length} rendering item{renderingItems.length > 1 ? 's' : ''} have a STEP file attached — ready to submit.
          </p>
        </div>
      )}

      {order.notes && (
        <div className="card p-4 mb-6 bg-status-info-bg border-border-default">
          <p className="text-sm text-status-info-text">{order.notes}</p>
        </div>
      )}

      {/* Render progress bar */}
      {rp && rp.total > 0 && (order.status === 'processing' || order.status === 'completed') && (
        <div className="card p-4 mb-6">
          <div className="flex items-center gap-3 mb-2">
            <h3 className="text-sm font-semibold text-content-secondary">Render Progress</h3>
            <span className="text-xs text-content-muted">
              {rp.completed}/{rp.total} completed
              {rp.processing > 0 && <span className="text-status-info-text ml-1">({rp.processing} rendering)</span>}
              {rp.failed > 0 && <span className="text-red-500 ml-1">({rp.failed} failed)</span>}
              {(rp as any).cancelled > 0 && <span className="text-orange-500 ml-1">({(rp as any).cancelled} cancelled)</span>}
            </span>
            {order.status === 'processing' && rp.processing > 0 && (
              <Loader2 size={14} className="animate-spin text-amber-500 ml-auto" />
            )}
          </div>
          <div className="flex h-2.5 rounded-full overflow-hidden bg-surface-muted">
            {rp.completed > 0 && (
              <div
                className="bg-green-500 transition-all duration-500"
                style={{ width: `${(rp.completed / rp.total) * 100}%` }}
              />
            )}
            {rp.processing > 0 && (
              <div
                className="bg-blue-500 transition-all duration-500"
                style={{ width: `${(rp.processing / rp.total) * 100}%` }}
              />
            )}
            {rp.failed > 0 && (
              <div
                className="bg-red-400 transition-all duration-500"
                style={{ width: `${(rp.failed / rp.total) * 100}%` }}
              />
            )}
            {(rp as any).cancelled > 0 && (
              <div
                className="bg-orange-300 transition-all duration-500"
                style={{ width: `${((rp as any).cancelled / rp.total) * 100}%` }}
              />
            )}
          </div>
        </div>
      )}

      {/* STEP upload section — only for draft orders */}
      {isDraft && (
        <div className="card p-6 mb-6">
          <div className="flex items-center gap-2 mb-1">
            <FileBox size={18} className="text-content-secondary" />
            <h2 className="font-semibold text-content">Upload STEP Files</h2>
          </div>
          <p className="text-sm text-content-secondary mb-4">
            Drop one or multiple .stp / .step files — they are matched to items by filename stem (case-insensitive).
          </p>
          <StepDropzone
            orderId={order.id}
            onMatchComplete={() => qc.invalidateQueries({ queryKey: ['order', id] })}
          />
        </div>
      )}

      {/* Source spreadsheet — reference data, collapsed by default after submission */}
      {order.source_excel != null && (
        <SourceSpreadsheet
          items={order.items}
          sourceExcel={order.source_excel}
          isDraft={isDraft}
          defaultOpen={isDraft}
          orderId={order.id}
          onItemUpdated={() => qc.invalidateQueries({ queryKey: ['order', id] })}
        />
      )}

      {/* ── Product Output Lines ─────────────────────────────────────────────────
           Shown ABOVE the Order Items table so the primary "what was ordered"
           view (product + output type + render status) is immediately visible  */}
      {((order.lines?.length ?? 0) > 0 || canGenerateLines) && (
        <div className="card mb-6">
          <div className="p-4 border-b border-border-default flex items-center justify-between">
            <h2 className="font-semibold text-content">
              Product Output Lines ({order.lines?.length ?? 0})
            </h2>
            {canGenerateLines && (
              <button
                className="btn-secondary text-xs flex items-center gap-1.5"
                onClick={() => setGenLinesOpen((v) => !v)}
              >
                <Wand2 size={13} />
                {genLinesOpen ? 'Cancel' : 'Assign Output Types…'}
              </button>
            )}
          </div>

          {/* Generate lines panel */}
          {canGenerateLines && genLinesOpen && (
            <div className="p-4 border-b border-border-default bg-status-warning-bg">
              <p className="text-sm text-status-warning-text mb-3">
                This order has <strong>{order.items?.length ?? 0} items</strong> but no output lines.
                Select one or more output types to generate renderable lines for all matching products:
              </p>
              {allOutputTypes.length === 0 ? (
                <p className="text-sm text-content-muted">Loading output types…</p>
              ) : (
                <div className="flex flex-wrap gap-3 mb-3">
                  {allOutputTypes.map((ot: OutputType) => (
                    <label key={ot.id} className="flex items-center gap-1.5 text-sm cursor-pointer">
                      <input
                        type="checkbox"
                        checked={!!genLinesSelected[ot.id]}
                        onChange={(e) =>
                          setGenLinesSelected((prev) => ({ ...prev, [ot.id]: e.target.checked }))
                        }
                      />
                      {ot.name}
                    </label>
                  ))}
                </div>
              )}
              <button
                className="btn-primary text-sm"
                disabled={generateLinesMut.isPending || !Object.values(genLinesSelected).some(Boolean)}
                onClick={() => generateLinesMut.mutate()}
              >
                <Plus size={14} />
                {generateLinesMut.isPending ? 'Generating…' : 'Generate Lines'}
              </button>
            </div>
          )}

          {(order.lines?.length ?? 0) > 0 && (
            <div className="overflow-auto">
              <table className="w-full text-sm">
                <thead>
                  <tr className="border-b border-border-light text-left">
                    <th className="px-4 py-2 font-medium text-content-secondary w-12">Thumb</th>
                    <th className="px-4 py-2 font-medium text-content-secondary">Product</th>
                    <th className="px-4 py-2 font-medium text-content-secondary">Output Type</th>
                    <th className="px-4 py-2 font-medium text-content-secondary">Backend</th>
                    <th className="px-4 py-2 font-medium text-content-secondary text-right">Price</th>
                    <th className="px-4 py-2 font-medium text-content-secondary">Render</th>
                    <th className="px-4 py-2 font-medium text-content-secondary">Status</th>
                    {isDraft && <th className="px-4 py-2 font-medium text-content-secondary w-10" />}
                  </tr>
                </thead>
                <tbody>
                  {order.lines.map((line: OrderLine) => (
                    <OrderLineRow
                      key={line.id}
                      line={line}
                      orderId={order.id}
                      isDraft={isDraft}
                      isPrivileged={isPrivileged}
                      onRemoved={() => qc.invalidateQueries({ queryKey: ['order', id] })}
                    />
                  ))}
                </tbody>
              </table>
            </div>
          )}
        </div>
      )}

      {/* Order items */}
      <div className="card">
        {/* Toolbar */}
        <div className="p-4 border-b border-border-default space-y-3">
          {/* Top row: title + view toggle */}
          <div className="flex items-center gap-3">
            <h2 className="font-semibold text-content">
              Order Items ({order.items.length})
            </h2>
            <div className="ml-auto flex items-center gap-1">
              <button
                onClick={() => setGalleryView(false)}
                className={`p-1.5 rounded ${!galleryView ? 'bg-surface-muted text-content' : 'text-content-muted hover:text-content-secondary'}`}
                title="Table view"
              >
                <LayoutList size={16} />
              </button>
              <button
                onClick={() => setGalleryView(true)}
                className={`p-1.5 rounded ${galleryView ? 'bg-surface-muted text-content' : 'text-content-muted hover:text-content-secondary'}`}
                title="Gallery view"
              >
                <LayoutGrid size={16} />
              </button>
            </div>
          </div>

          {/* Filter bar — only in table view */}
          {!galleryView && (
            <div className="flex flex-wrap items-center gap-2">
              {/* Text search */}
              <div className="relative flex-1 min-w-[180px]">
                <Search size={14} className="absolute left-2.5 top-1/2 -translate-y-1/2 text-content-muted pointer-events-none" />
                <input
                  type="text"
                  placeholder="Search name, Baureihe, Ebene…"
                  value={filters.search}
                  onChange={(e) => updateFilter('search', e.target.value)}
                  className="w-full pl-8 pr-3 py-1.5 text-sm border border-border-default rounded-md focus:outline-none focus:ring-2 focus:ring-accent"
                />
              </div>

              {/* Item status filter */}
              <select
                value={filters.itemStatus}
                onChange={(e) => updateFilter('itemStatus', e.target.value as TableFilters['itemStatus'])}
                className="py-1.5 pl-2 pr-7 text-sm border border-border-default rounded-md focus:outline-none focus:ring-2 focus:ring-accent bg-surface"
              >
                <option value="">All statuses</option>
                <option value="pending">Pending</option>
                <option value="approved">Approved</option>
                <option value="rejected">Rejected</option>
              </select>

              {/* STEP status filter */}
              <select
                value={filters.stepStatus}
                onChange={(e) => updateFilter('stepStatus', e.target.value as TableFilters['stepStatus'])}
                className="py-1.5 pl-2 pr-7 text-sm border border-border-default rounded-md focus:outline-none focus:ring-2 focus:ring-accent bg-surface"
              >
                <option value="">All STEP</option>
                <option value="with_step">With STEP</option>
                <option value="missing_step">Missing STEP</option>
              </select>

              {/* Rendering filter */}
              <select
                value={filters.rendering}
                onChange={(e) => updateFilter('rendering', e.target.value as TableFilters['rendering'])}
                className="py-1.5 pl-2 pr-7 text-sm border border-border-default rounded-md focus:outline-none focus:ring-2 focus:ring-accent bg-surface"
              >
                <option value="">All rendering</option>
                <option value="rendering">Rendering only</option>
                <option value="non_rendering">Non-rendering</option>
              </select>

              {/* Active filter count + clear */}
              {activeFilterCount > 0 && (
                <button
                  onClick={() => setFilters(EMPTY_FILTERS)}
                  className="flex items-center gap-1.5 px-2.5 py-1.5 text-sm text-content-secondary bg-surface-muted hover:bg-surface-hover rounded-md transition-colors"
                >
                  <X size={13} />
                  Clear
                  <span className="ml-0.5 inline-flex items-center justify-center w-4 h-4 rounded-full bg-accent text-white text-[10px] font-bold">
                    {activeFilterCount}
                  </span>
                </button>
              )}

              {activeFilterCount === 0 && (
                <div className="flex items-center gap-1 text-xs text-content-muted ml-1">
                  <SlidersHorizontal size={13} />
                  <span>Filter</span>
                </div>
              )}
            </div>
          )}
        </div>

        {galleryView ? (
          <>
            <div className="p-4 grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 xl:grid-cols-6 gap-3">
              {order.items.map((item: any) => (
                <GalleryCard
                  key={item.id}
                  item={item}
                  isDraft={isDraft}
                  onClick={() => setGallerySelected(item)}
                />
              ))}
            </div>
            {gallerySelected && (
              <GalleryModal
                item={gallerySelected}
                orderId={order.id}
                isDraft={isDraft}
                onClose={() => setGallerySelected(null)}
                onUnlink={() => { setGallerySelected(null); qc.invalidateQueries({ queryKey: ['order', id] }) }}
                onRerender={() => qc.invalidateQueries({ queryKey: ['order', id] })}
              />
            )}
          </>
        ) : (
          <OrderItemsTable
            items={order.items}
            orderId={order.id}
            isDraft={isDraft}
            filters={filters}
            sortKey={sortKey}
            sortDir={sortDir}
            onSort={handleSort}
            expandedId={expandedId}
            onExpand={setExpandedId}
            onUnlink={() => qc.invalidateQueries({ queryKey: ['order', id] })}
          />
        )}
      </div>

    </div>
  )
}

// ── Order Line Row ────────────────────────────────────────────────────────────

function OrderLineRow({
  line, orderId, isDraft, isPrivileged, onRemoved,
}: {
  line: OrderLine
  orderId: string
  isDraft: boolean
  isPrivileged: boolean
  onRemoved: () => void
}) {
  const qc = useQueryClient()
  const removeMut = useMutation({
    mutationFn: () => removeOrderLine(orderId, line.id),
    onSuccess: onRemoved,
    onError: (e: any) => toast.error(e.response?.data?.detail || 'Remove failed'),
  })

  const cancelMut = useMutation({
    mutationFn: () => cancelLineRender(orderId, line.id),
    onSuccess: () => {
      toast.success('Render cancelled')
      qc.invalidateQueries({ queryKey: ['order', orderId] })
    },
    onError: (e: any) => toast.error(e.response?.data?.detail || 'Cancel failed'),
  })

  const canCancel = isPrivileged && (line.render_status === 'processing' || line.render_status === 'pending') && line.output_type_id

  const renderStatusColor: Record<string, string> = {
    pending: 'bg-surface-muted text-content-muted',
    processing: 'bg-status-info-bg text-status-info-text',
    completed: 'bg-status-success-bg text-status-success-text',
    failed: 'bg-status-error-bg text-status-error-text',
    cancelled: 'bg-status-warning-bg text-status-warning-text',
  }

  return (
    <tr className="border-b border-border-light hover:bg-surface-hover">
      {/* Thumbnail */}
      <td className="px-4 py-2">
        {line.thumbnail_url ? (
          <img
            src={line.thumbnail_url}
            alt={line.product.name || ''}
            className="w-10 h-10 object-contain rounded border bg-surface"
          />
        ) : (
          <div className="w-10 h-10 rounded border border-dashed border-border-default bg-surface-alt flex items-center justify-center">
            <Box size={16} className="text-content-muted" />
          </div>
        )}
      </td>

      {/* Product */}
      <td className="px-4 py-2">
        <Link
          to={`/products/${line.product_id}`}
          className="font-medium text-accent hover:underline"
          onClick={(e) => e.stopPropagation()}
        >
          {line.product.name || line.product.pim_id}
        </Link>
        <span className="text-xs text-content-muted font-mono">{line.product.pim_id}</span>
      </td>

      {/* Output Type + Position */}
      <td className="px-4 py-2">
        <div className="flex flex-col gap-1">
          {line.output_type ? (
            <span className="text-xs px-2 py-0.5 rounded-full bg-accent-light text-accent font-medium w-fit">
              {line.output_type.name}
            </span>
          ) : (
            <span className="text-xs text-content-muted italic">tracking only</span>
          )}
          {line.render_position_name && (
            <span className="text-xs px-2 py-0.5 rounded-full bg-purple-100 text-purple-700 font-medium w-fit">
              {line.render_position_name}
            </span>
          )}
        </div>
      </td>

      {/* Backend */}
      <td className="px-4 py-2">
        {line.render_backend_used ? (
          line.render_backend_used === 'flamenco' && line.flamenco_job_id ? (
            <a
              href="http://localhost:8080"
              target="_blank"
              rel="noopener noreferrer"
              onClick={(e) => e.stopPropagation()}
              className="inline-flex items-center gap-1 text-xs px-2 py-0.5 rounded-full bg-status-warning-bg text-status-warning-text font-medium hover:bg-surface-hover transition-colors"
            >
              Flamenco <ExternalLink size={9} />
            </a>
          ) : line.render_backend_used === 'celery' ? (
            <span className="text-xs px-2 py-0.5 rounded-full bg-status-info-bg text-status-info-text font-medium">
              Celery
            </span>
          ) : (
            <span className="text-xs text-content-muted">{line.render_backend_used}</span>
          )
        ) : (
          <span className="text-xs text-content-muted">—</span>
        )}
      </td>

      {/* Price */}
      <td className="px-4 py-2 text-right">
        {line.unit_price != null ? (
          <span className="text-sm font-medium text-content-secondary">{line.unit_price.toFixed(2)}</span>
        ) : (
          <span className="text-xs text-content-muted">—</span>
        )}
      </td>

      {/* Render Status */}
      <td className="px-4 py-2">
        {line.output_type_id ? (
          <div>
            <div className="flex items-center gap-1.5">
              <span className={`inline-flex items-center gap-1 text-xs px-2 py-0.5 rounded-full font-medium ${renderStatusColor[line.render_status] || 'bg-surface-muted text-content-muted'}`}>
                {line.render_status === 'processing' && <Loader2 size={10} className="animate-spin" />}
                {line.render_status}
              </span>
              {canCancel && (
                <button
                  onClick={(e) => {
                    e.stopPropagation()
                    cancelMut.mutate()
                  }}
                  disabled={cancelMut.isPending}
                  className="text-content-muted hover:text-orange-500 transition-colors"
                  title="Cancel this render"
                >
                  {cancelMut.isPending ? <Loader2 size={12} className="animate-spin" /> : <Ban size={12} />}
                </button>
              )}
            </div>
            <LiveRenderLog
              orderLineId={line.id}
              isActive={line.render_status === 'processing'}
              compact
            />
          </div>
        ) : (
          <span className="text-xs text-content-muted">—</span>
        )}
      </td>

      {/* Item Status */}
      <td className="px-4 py-2">
        <ItemStatusBadge status={line.item_status} />
      </td>

      {/* Remove (draft only) */}
      {isDraft && (
        <td className="px-4 py-2">
          <button
            className="text-content-muted hover:text-red-500"
            onClick={() => { if (confirm('Remove this line?')) removeMut.mutate() }}
            disabled={removeMut.isPending}
            title="Remove this output line from the order"
          >
            <X size={14} />
          </button>
        </td>
      )}
    </tr>
  )
}

// ── Table component ───────────────────────────────────────────────────────────

interface OrderItemsTableProps {
  items: OrderItem[]
  orderId: string
  isDraft: boolean
  filters: TableFilters
  sortKey: SortKey
  sortDir: SortDir
  onSort: (key: SortKey) => void
  expandedId: string | null
  onExpand: (id: string | null) => void
  onUnlink: () => void
}

function OrderItemsTable({
  items, orderId, isDraft, filters, sortKey, sortDir, onSort,
  expandedId, onExpand, onUnlink,
}: OrderItemsTableProps) {
  const filtered = useMemo(() => {
    let result = [...items]

    // Text search
    if (filters.search.trim()) {
      const q = filters.search.trim().toLowerCase()
      result = result.filter((item) =>
        [item.name_cad_modell, item.baureihe, item.ebene1, item.ebene2]
          .some((v) => v?.toLowerCase().includes(q))
      )
    }

    // Item status
    if (filters.itemStatus) {
      result = result.filter((item) => item.item_status === filters.itemStatus)
    }

    // STEP status
    if (filters.stepStatus === 'with_step') {
      result = result.filter((item) => !!item.cad_file_id)
    } else if (filters.stepStatus === 'missing_step') {
      result = result.filter((item) => !item.cad_file_id)
    }

    // Rendering filter
    if (filters.rendering === 'rendering') {
      result = result.filter((item) => item.medias_rendering === true)
    } else if (filters.rendering === 'non_rendering') {
      result = result.filter((item) => item.medias_rendering !== true)
    }

    // Sort
    result.sort((a, b) => {
      let av: string | number = ''
      let bv: string | number = ''
      if (sortKey === 'row_index') { av = a.row_index; bv = b.row_index }
      else if (sortKey === 'name_cad_modell') { av = a.name_cad_modell ?? ''; bv = b.name_cad_modell ?? '' }
      else if (sortKey === 'baureihe') { av = a.baureihe ?? ''; bv = b.baureihe ?? '' }
      else if (sortKey === 'item_status') { av = a.item_status ?? ''; bv = b.item_status ?? '' }

      if (av < bv) return sortDir === 'asc' ? -1 : 1
      if (av > bv) return sortDir === 'asc' ? 1 : -1
      return 0
    })

    return result
  }, [items, filters, sortKey, sortDir])

  if (filtered.length === 0) {
    return (
      <div className="p-12 text-center text-content-muted text-sm">
        No items match the current filters.
      </div>
    )
  }

  return (
    <div className="overflow-x-auto">
      <table className="w-full text-sm">
        <thead className="sticky top-0 z-10 bg-surface-alt border-b border-border-default">
          <tr>
            <SortableTh label="#" sortKey="row_index" current={sortKey} dir={sortDir} onSort={onSort} className="w-12 text-center" />
            <th className="py-2.5 px-3 text-left text-xs font-semibold text-content-secondary uppercase tracking-wide w-10">
              Img
            </th>
            <SortableTh label="CAD Model" sortKey="name_cad_modell" current={sortKey} dir={sortDir} onSort={onSort} className="min-w-[160px]" />
            <SortableTh label="Baureihe" sortKey="baureihe" current={sortKey} dir={sortDir} onSort={onSort} className="min-w-[110px]" />
            <th className="py-2.5 px-3 text-left text-xs font-semibold text-content-secondary uppercase tracking-wide">Ebene 1</th>
            <th className="py-2.5 px-3 text-left text-xs font-semibold text-content-secondary uppercase tracking-wide">Ebene 2</th>
            <th className="py-2.5 px-3 text-left text-xs font-semibold text-content-secondary uppercase tracking-wide">Lagertyp</th>
            <th className="py-2.5 px-3 text-center text-xs font-semibold text-content-secondary uppercase tracking-wide">Rendering</th>
            <th className="py-2.5 px-3 text-center text-xs font-semibold text-content-secondary uppercase tracking-wide">Parts</th>
            <th className="py-2.5 px-3 text-center text-xs font-semibold text-content-secondary uppercase tracking-wide">STEP</th>
            <SortableTh label="Status" sortKey="item_status" current={sortKey} dir={sortDir} onSort={onSort} className="text-center" />
          </tr>
        </thead>
        <tbody className="divide-y divide-border-light">
          {filtered.map((item) => (
            <ItemTableRow
              key={item.id}
              item={item}
              orderId={orderId}
              isDraft={isDraft}
              expanded={expandedId === item.id}
              onExpand={() => onExpand(expandedId === item.id ? null : item.id)}
              onUnlink={onUnlink}
            />
          ))}
        </tbody>
      </table>
    </div>
  )
}

// ── Sortable table header ─────────────────────────────────────────────────────

function SortableTh({
  label, sortKey: key, current, dir, onSort, className = '',
}: {
  label: string
  sortKey: SortKey
  current: SortKey
  dir: SortDir
  onSort: (k: SortKey) => void
  className?: string
}) {
  const active = current === key
  return (
    <th
      className={`py-2.5 px-3 text-left text-xs font-semibold text-content-secondary uppercase tracking-wide cursor-pointer select-none hover:text-content transition-colors ${className}`}
      onClick={() => onSort(key)}
    >
      <span className="flex items-center gap-1">
        {label}
        {active ? (
          dir === 'asc' ? <ChevronUp size={12} className="text-accent" /> : <ChevronDown size={12} className="text-accent" />
        ) : (
          <ChevronsUpDown size={12} className="text-content-muted" />
        )}
      </span>
    </th>
  )
}

// ── Table row + expand panel ──────────────────────────────────────────────────

function ItemTableRow({
  item, orderId, isDraft, expanded, onExpand, onUnlink,
}: {
  item: OrderItem
  orderId: string
  isDraft: boolean
  expanded: boolean
  onExpand: () => void
  onUnlink: () => void
}) {
  const hasStep = !!item.cad_file_id
  const [imgError, setImgError] = useState(false)
  const [thumbVersion, setThumbVersion] = useState(0)

  const unlinkMut = useMutation({
    mutationFn: () => unlinkCadFile(orderId, item.id),
    onSuccess: () => { toast.success('CAD file removed'); onUnlink() },
    onError: (e: any) => toast.error(e.response?.data?.detail || 'Remove failed'),
  })

  const rerenderMut = useMutation({
    mutationFn: () => regenerateItemThumbnail(orderId, item.id),
    onSuccess: () => {
      toast.success('Thumbnail re-render queued — refresh in a moment')
      setTimeout(() => setThumbVersion((v) => v + 1), 8000)
    },
    onError: (e: any) => toast.error(e.response?.data?.detail || 'Re-render failed'),
  })

  const thumbSrc = `/api/cad/${item.cad_file_id}/thumbnail${thumbVersion > 0 ? `?v=${thumbVersion}` : ''}`

  return (
    <>
      <tr
        onClick={onExpand}
        className={`cursor-pointer transition-colors hover:bg-surface-hover ${expanded ? 'bg-accent-light' : ''}`}
      >
        {/* Row # */}
        <td className="py-2.5 px-3 text-center text-content-muted text-xs font-mono">{item.row_index}</td>

        {/* Thumbnail 40px */}
        <td className="py-2 px-3">
          <div className="w-10 h-10 rounded border border-border-default bg-surface-alt flex items-center justify-center overflow-hidden shrink-0">
            {hasStep && !imgError ? (
              <img
                src={thumbSrc}
                alt="thumb"
                className="w-full h-full object-contain"
                onError={() => setImgError(true)}
              />
            ) : (
              <ImageIcon size={16} className="text-content-muted" />
            )}
          </div>
        </td>

        {/* CAD Model */}
        <td className="py-2.5 px-3 font-mono text-xs text-content max-w-[200px]">
          <p className="truncate" title={item.name_cad_modell ?? undefined}>{item.name_cad_modell || '—'}</p>
        </td>

        {/* Baureihe */}
        <td className="py-2.5 px-3 text-content-secondary text-xs max-w-[130px]">
          <p className="truncate">{item.baureihe || '—'}</p>
        </td>

        {/* Ebene 1 */}
        <td className="py-2.5 px-3 text-content-secondary text-xs max-w-[120px]">
          <p className="truncate">{item.ebene1 || '—'}</p>
        </td>

        {/* Ebene 2 */}
        <td className="py-2.5 px-3 text-content-secondary text-xs max-w-[120px]">
          <p className="truncate">{item.ebene2 || '—'}</p>
        </td>

        {/* Lagertyp */}
        <td className="py-2.5 px-3 text-content-secondary text-xs max-w-[110px]">
          <p className="truncate">{item.lagertyp || '—'}</p>
        </td>

        {/* Rendering */}
        <td className="py-2.5 px-3 text-center">
          {item.medias_rendering
            ? <span className="text-xs font-medium text-accent">Yes</span>
            : <span className="text-xs text-content-muted">No</span>}
        </td>

        {/* Components count */}
        <td className="py-2.5 px-3 text-center text-xs text-content-secondary">
          {(item.components?.length ?? 0) || '—'}
        </td>

        {/* STEP status */}
        <td className="py-2.5 px-3 text-center">
          {hasStep ? (
            <span className="badge badge-green">STEP</span>
          ) : (
            <span
              className={`inline-block w-2 h-2 rounded-full ${
                item.medias_rendering ? 'bg-amber-400' : 'bg-gray-300'
              }`}
              title={item.medias_rendering ? 'Missing STEP (required)' : 'No STEP (optional)'}
            />
          )}
        </td>

        {/* Item status */}
        <td className="py-2.5 px-3 text-center">
          <ItemStatusBadge status={item.item_status} />
        </td>
      </tr>

      {/* Expanded detail panel */}
      {expanded && (
        <tr>
          <td colSpan={11} className="bg-surface-alt border-t border-b border-border-light px-0 py-0">
            <div className="px-6 py-5">
              <div className="flex gap-6">
                {/* Thumbnail */}
                <div className="shrink-0">
                  <p className="text-xs text-content-muted mb-1">CAD Thumbnail</p>
                  {hasStep ? (
                    <div className="flex flex-col items-start gap-2">
                      <ThumbnailPreview cadFileId={item.cad_file_id!} version={thumbVersion} />
                      <div className="flex items-center gap-3">
                        <button
                          onClick={(e) => { e.stopPropagation(); rerenderMut.mutate() }}
                          disabled={rerenderMut.isPending}
                          className="flex items-center gap-1 text-xs text-accent hover:text-accent-hover disabled:opacity-50"
                          title="Re-render thumbnail with current part colours"
                        >
                          <RotateCcw size={12} className={rerenderMut.isPending ? 'animate-spin' : ''} />
                          {rerenderMut.isPending ? 'Queuing…' : 'Re-render'}
                        </button>
                        {isDraft && (
                          <button
                            onClick={(e) => {
                              e.stopPropagation()
                              if (confirm('Remove this STEP file from the item?')) unlinkMut.mutate()
                            }}
                            disabled={unlinkMut.isPending}
                            className="flex items-center gap-1 text-xs text-red-500 hover:text-red-700 disabled:opacity-50"
                            title="Remove STEP file"
                          >
                            <Unlink size={12} />
                            {unlinkMut.isPending ? 'Removing…' : 'Remove STEP'}
                          </button>
                        )}
                      </div>
                    </div>
                  ) : (
                    <div className="w-32 h-32 rounded border border-dashed border-border-default bg-surface-muted flex flex-col items-center justify-center text-content-muted">
                      <ImageIcon size={24} className="mb-1" />
                      <span className="text-xs text-center px-2">No STEP file yet</span>
                    </div>
                  )}
                </div>

                <div className="flex-1 grid grid-cols-2 md:grid-cols-3 gap-4 text-sm">
                  <Field label="Ebene 1" value={item.ebene1} />
                  <Field label="PIM-ID" value={item.pim_id} />
                  <Field label="Produkt / Baureihe" value={item.produkt_baureihe} />
                  <Field label="Gewähltes Produkt" value={item.gewaehltes_produkt} />
                  <Field label="Bildnummer" value={item.gewuenschte_bildnummer} />
                  <Field label="Lagertyp" value={item.lagertyp} />
                  <Field label="Medias Rendering" value={item.medias_rendering?.toString() ?? '—'} />
                </div>
              </div>

              {item.components && item.components.length > 0 && (
                <div className="mt-4">
                  <p className="text-xs font-semibold text-content-secondary uppercase tracking-wide mb-2">
                    Components ({item.components.length})
                  </p>
                  <div className="overflow-x-auto">
                    <table className="text-xs w-full">
                      <thead>
                        <tr className="text-content-muted">
                          <th className="text-left py-1 pr-4 font-medium">#</th>
                          <th className="text-left py-1 pr-4 font-medium">Type</th>
                          <th className="text-left py-1 pr-4 font-medium">Part Name</th>
                          <th className="text-left py-1 font-medium">Material</th>
                        </tr>
                      </thead>
                      <tbody className="divide-y divide-border-light">
                        {item.components.map((c: any, i: number) => (
                          <tr key={i}>
                            <td className="py-1 pr-4 text-content-muted">{i + 1}</td>
                            <td className="py-1 pr-4 text-content-secondary truncate max-w-[160px]">{c.component_type || '—'}</td>
                            <td className="py-1 pr-4 font-mono text-content truncate max-w-[200px]">{c.part_name || '—'}</td>
                            <td className="py-1 text-content-secondary">{c.material || '—'}</td>
                          </tr>
                        ))}
                      </tbody>
                    </table>
                  </div>
                </div>
              )}

              {/* CAD part material assignments */}
              {(item as any).cad_parsed_objects && (item as any).cad_parsed_objects.length > 0 && (
                <CadPartMaterials
                  orderId={orderId}
                  itemId={item.id}
                  partNames={(item as any).cad_parsed_objects}
                  savedMaterials={(item as any).cad_part_materials ?? []}
                  excelComponents={item.components}
                />
              )}
            </div>
          </td>
        </tr>
      )}
    </>
  )
}

// ── Reusable display helpers ──────────────────────────────────────────────────

function ThumbnailPreview({ cadFileId, version = 0 }: { cadFileId: string; version?: number }) {
  const [imgError, setImgError] = useState(false)
  const src = `/api/cad/${cadFileId}/thumbnail${version > 0 ? `?v=${version}` : ''}`

  if (imgError) {
    return (
      <div className="w-32 h-32 rounded border border-dashed border-border-default bg-surface-muted flex flex-col items-center justify-center text-content-muted">
        <ImageIcon size={24} className="mb-1" />
        <span className="text-xs text-center px-2">No preview</span>
      </div>
    )
  }

  return (
    <div className="relative shrink-0 cursor-zoom-in group">
      {/* Small thumbnail */}
      <img
        src={src}
        alt="CAD thumbnail"
        className="w-32 h-32 object-contain rounded border border-border-default bg-surface"
        onError={() => setImgError(true)}
      />

      {/* Full-res hover overlay */}
      <div className="absolute left-full top-1/2 -translate-y-1/2 z-50
                      invisible group-hover:visible pl-4">
        {/* Arrow pointing left */}
        <div className="absolute left-4 top-1/2 -translate-y-1/2 -ml-px
                        border-8 border-transparent border-r-white drop-shadow-sm" />
        <div className="rounded-xl border border-border-default bg-surface shadow-2xl p-2 w-[520px]">
          <img
            src={src}
            alt="CAD preview"
            className="w-full h-auto rounded-lg object-contain"
          />
          <p className="text-[10px] text-content-muted text-center mt-1 pb-0.5">Full resolution preview</p>
        </div>
      </div>
    </div>
  )
}

function Field({ label, value }: { label: string; value: string | null | undefined }) {
  return (
    <div>
      <p className="text-xs text-content-muted">{label}</p>
      <p className="text-content">{value || '—'}</p>
    </div>
  )
}

function StatusBadge({ status }: { status: string }) {
  const map: Record<string, string> = {
    draft: 'badge-gray', submitted: 'badge-blue', processing: 'badge-yellow',
    completed: 'badge-green', rejected: 'badge-red',
  }
  return <span className={`badge ${map[status] ?? 'badge-gray'}`}>{status}</span>
}

function ItemStatusBadge({ status }: { status: string }) {
  const map: Record<string, string> = {
    pending: 'badge-gray', approved: 'badge-green', rejected: 'badge-red',
  }
  return <span className={`badge ${map[status] ?? 'badge-gray'}`}>{status}</span>
}

// ── Gallery view ─────────────────────────────────────────────────────────────

function GalleryCard({ item, isDraft, onClick }: { item: any; isDraft: boolean; onClick: () => void }) {
  const hasStep = !!item.cad_file_id
  const [imgError, setImgError] = useState(false)

  return (
    <div
      onClick={onClick}
      className="cursor-pointer rounded-lg border border-border-default hover:border-accent bg-surface overflow-hidden transition-colors group"
    >
      <div className="aspect-square bg-surface-alt relative overflow-hidden">
        {hasStep && !imgError ? (
          <img
            src={`/api/cad/${item.cad_file_id}/thumbnail`}
            alt={item.name_cad_modell || 'thumbnail'}
            className="w-full h-full object-contain group-hover:scale-105 transition-transform duration-200"
            onError={() => setImgError(true)}
          />
        ) : (
          <div className="w-full h-full flex items-center justify-center text-content-muted">
            <ImageIcon size={32} />
          </div>
        )}
        {/* Status dot */}
        {isDraft && (
          <span
            className={`absolute top-1.5 right-1.5 w-2.5 h-2.5 rounded-full border-2 border-white ${
              hasStep ? 'bg-green-400' : item.medias_rendering ? 'bg-amber-400' : 'bg-gray-300'
            }`}
          />
        )}
      </div>
      <div className="p-2 border-t border-border-light">
        <p className="text-xs font-mono truncate text-content-secondary" title={item.name_cad_modell || undefined}>
          {item.name_cad_modell || '—'}
        </p>
        <div className="flex items-center gap-1 mt-1">
          <ItemStatusBadge status={item.item_status} />
          {hasStep && <span className="badge badge-green text-xs">STEP</span>}
        </div>
      </div>
    </div>
  )
}

// ── Source Spreadsheet ────────────────────────────────────────────────────────

const STD_COLS: { key: keyof OrderItem; label: string; editable?: boolean }[] = [
  { key: 'ebene1',                 label: 'Ebene 1',         editable: true },
  { key: 'ebene2',                 label: 'Ebene 2',         editable: true },
  { key: 'baureihe',               label: 'Baureihe',        editable: true },
  { key: 'pim_id',                 label: 'PIM-ID',          editable: true },
  { key: 'produkt_baureihe',       label: 'Produkt/Baureihe',editable: true },
  { key: 'gewaehltes_produkt',     label: 'Gew. Produkt',    editable: true },
  { key: 'name_cad_modell',        label: 'CAD-Modell',      editable: true },
  { key: 'gewuenschte_bildnummer', label: 'Bildnummer',      editable: true },
  { key: 'lagertyp',               label: 'Lagertyp',        editable: true },
]

function SourceSpreadsheet({
  items,
  sourceExcel,
  isDraft,
  defaultOpen = true,
  orderId,
  onItemUpdated,
}: {
  items: OrderItem[]
  sourceExcel: string | null | undefined
  isDraft?: boolean
  defaultOpen?: boolean
  orderId?: string
  onItemUpdated?: () => void
}) {
  const [open, setOpen] = useState(defaultOpen)
  const [saving, setSaving] = useState<string | null>(null) // itemId being saved
  const sorted = useMemo(() => [...items].sort((a, b) => a.row_index - b.row_index), [items])
  const maxComponents = Math.max(0, ...sorted.map((i) => i.components?.length ?? 0))

  const fileName = sourceExcel
    ? sourceExcel.replace(/\\/g, '/').split('/').pop() ?? sourceExcel
    : null

  async function saveField(itemId: string, field: keyof OrderItem, value: string | boolean | null) {
    if (!orderId) return
    setSaving(itemId)
    try {
      await patchOrderItem(orderId, itemId, { [field]: value })
      onItemUpdated?.()
    } catch {
      toast.error('Failed to save change')
    } finally {
      setSaving(null)
    }
  }

  return (
    <div className="card mb-6 overflow-hidden">
      {/* Collapsible header */}
      <button
        className="w-full flex items-center gap-2 px-4 py-3 hover:bg-surface-hover transition-colors"
        onClick={() => setOpen((v) => !v)}
      >
        <FileSpreadsheet size={16} className="text-accent shrink-0" />
        <span className="font-semibold text-content text-sm">Source Spreadsheet</span>
        {fileName && (
          <span className="text-xs text-content-muted font-mono ml-1 truncate max-w-[300px]">{fileName}</span>
        )}
        <span className="ml-auto flex items-center gap-2 text-xs text-content-muted shrink-0">
          {isDraft && <span className="text-accent font-medium">editable</span>}
          {sorted.length} row{sorted.length !== 1 ? 's' : ''}
          {open ? <ChevronUp size={14} /> : <ChevronDown size={14} />}
        </span>
      </button>

      {open && (
        <div className="overflow-x-auto border-t border-border-light">
          <table className="text-xs whitespace-nowrap border-separate border-spacing-0">
            <thead>
              <tr>
                <th className="sticky left-0 z-20 bg-surface-alt py-2 px-3 text-left font-semibold text-content-secondary uppercase tracking-wide border-b border-r border-border-default min-w-[40px]">
                  #
                </th>
                {/* Rendering toggle column */}
                <th className="bg-surface-alt py-2 px-3 text-center font-semibold text-content-secondary uppercase tracking-wide border-b border-border-default min-w-[80px]">
                  Rendering
                </th>
                {STD_COLS.map((c) => (
                  <th key={c.key} className="bg-surface-alt py-2 px-3 text-left font-semibold text-content-secondary uppercase tracking-wide border-b border-border-default">
                    {c.label}
                  </th>
                ))}
                {Array.from({ length: maxComponents }, (_, i) => (
                  <Fragment key={`ch-${i}`}>
                    <th className="bg-surface-alt py-2 px-3 text-left font-semibold text-content-secondary uppercase tracking-wide border-b border-l border-border-default">
                      Bauteil {i + 1}
                    </th>
                    <th className="bg-surface-alt py-2 px-3 text-left font-semibold text-content-secondary uppercase tracking-wide border-b border-border-default">
                      Material {i + 1}
                    </th>
                  </Fragment>
                ))}
              </tr>
            </thead>
            <tbody>
              {sorted.map((item) => (
                <tr
                  key={item.id}
                  className={`hover:bg-surface-hover/30 group ${saving === item.id ? 'opacity-60' : ''}`}
                >
                  {/* Row # */}
                  <td className="sticky left-0 z-10 bg-surface group-hover:bg-surface-hover/30 py-1.5 px-3 font-mono text-content-muted border-r border-b border-border-light">
                    {item.row_index}
                  </td>

                  {/* Rendering toggle */}
                  <td className="py-1.5 px-3 text-center border-b border-border-light">
                    {isDraft ? (
                      <button
                        onClick={() => saveField(item.id, 'medias_rendering', !item.medias_rendering)}
                        disabled={saving === item.id}
                        className={`inline-flex items-center justify-center w-5 h-5 rounded border-2 transition-colors ${
                          item.medias_rendering
                            ? 'text-white'
                            : 'border-border-default bg-surface hover:border-accent'
                        }`}
                        style={item.medias_rendering ? { backgroundColor: 'var(--color-accent)', borderColor: 'var(--color-accent)' } : undefined}
                        title={item.medias_rendering ? 'Click to disable rendering' : 'Click to enable rendering'}
                      >
                        {item.medias_rendering && (
                          <svg viewBox="0 0 10 8" className="w-3 h-3 fill-current">
                            <path d="M1 4l3 3 5-6" stroke="currentColor" strokeWidth="1.5" fill="none" strokeLinecap="round" strokeLinejoin="round"/>
                          </svg>
                        )}
                      </button>
                    ) : (
                      <span className={item.medias_rendering ? 'text-accent font-medium' : 'text-content-muted'}>
                        {item.medias_rendering ? 'Yes' : 'No'}
                      </span>
                    )}
                  </td>

                  {/* Text fields */}
                  {STD_COLS.map((c) => {
                    const raw = (item as any)[c.key]
                    const display = raw ?? ''
                    return (
                      <td key={c.key} className="py-0.5 px-1 border-b border-border-light">
                        {isDraft && c.editable ? (
                          <input
                            type="text"
                            defaultValue={display}
                            onBlur={(e) => {
                              const newVal = e.target.value.trim() || null
                              if (newVal !== (raw ?? null)) {
                                saveField(item.id, c.key, newVal)
                              }
                            }}
                            onKeyDown={(e) => {
                              if (e.key === 'Enter') (e.target as HTMLInputElement).blur()
                            }}
                            className="w-full min-w-[120px] max-w-[200px] px-1.5 py-0.5 text-xs text-content-secondary rounded border border-transparent hover:border-border-default focus:border-accent focus:outline-none focus:bg-surface bg-transparent"
                          />
                        ) : (
                          <span className="block px-1.5 py-0.5 max-w-[200px] truncate text-content-secondary" title={display || undefined}>
                            {display || '—'}
                          </span>
                        )}
                      </td>
                    )
                  })}

                  {/* Component columns (read-only) */}
                  {Array.from({ length: maxComponents }, (_, i) => {
                    const comp = (item.components as any[])?.[i]
                    return (
                      <Fragment key={`cv-${i}`}>
                        <td className="py-1.5 px-3 font-mono text-content-secondary border-b border-l border-border-light">
                          <span className="block max-w-[200px] truncate" title={comp?.part_name ?? undefined}>
                            {comp?.part_name || '—'}
                          </span>
                        </td>
                        <td className="py-1.5 px-3 text-content-secondary border-b border-border-light">
                          <span className="block max-w-[140px] truncate" title={comp?.material ?? undefined}>
                            {comp?.material || '—'}
                          </span>
                        </td>
                      </Fragment>
                    )
                  })}
                </tr>
              ))}
            </tbody>
          </table>
        </div>
      )}
    </div>
  )
}

// ── Gallery modal ─────────────────────────────────────────────────────────────

function GalleryModal({
  item, orderId, isDraft, onClose, onUnlink, onRerender,
}: {
  item: any; orderId: string; isDraft: boolean
  onClose: () => void; onUnlink: () => void; onRerender: () => void
}) {
  const hasStep = !!item.cad_file_id
  const [thumbVersion, setThumbVersion] = useState(0)

  const unlinkMut = useMutation({
    mutationFn: () => unlinkCadFile(orderId, item.id),
    onSuccess: () => { toast.success('CAD file removed'); onUnlink() },
    onError: (e: any) => toast.error(e.response?.data?.detail || 'Remove failed'),
  })

  const rerenderMut = useMutation({
    mutationFn: () => regenerateItemThumbnail(orderId, item.id),
    onSuccess: () => {
      toast.success('Re-render queued — refreshing in a moment')
      setTimeout(() => { setThumbVersion((v) => v + 1); onRerender() }, 8000)
    },
    onError: (e: any) => toast.error(e.response?.data?.detail || 'Re-render failed'),
  })

  return (
    <div
      className="fixed inset-0 z-50 flex items-center justify-center bg-black/50 p-4"
      onClick={onClose}
    >
      <div
        className="bg-surface rounded-2xl shadow-2xl max-w-lg w-full overflow-hidden"
        onClick={(e) => e.stopPropagation()}
      >
        {/* Header */}
        <div className="flex items-center gap-3 px-5 py-4 border-b border-border-default">
          <p className="font-mono font-semibold text-content flex-1 truncate">
            {item.name_cad_modell || `Row ${item.row_index}`}
          </p>
          <ItemStatusBadge status={item.item_status} />
          <button onClick={onClose} className="text-content-muted hover:text-content-secondary p-1">
            <X size={18} />
          </button>
        </div>

        {/* Thumbnail */}
        <div className="p-4 bg-surface-alt flex items-center justify-center min-h-[280px]">
          {hasStep ? (
            <img
              src={`/api/cad/${item.cad_file_id}/thumbnail${thumbVersion > 0 ? `?v=${thumbVersion}` : ''}`}
              alt="CAD preview"
              className="max-h-64 object-contain rounded-lg"
            />
          ) : (
            <div className="flex flex-col items-center text-content-muted py-8">
              <ImageIcon size={48} className="mb-2" />
              <p className="text-sm">No STEP file attached</p>
            </div>
          )}
        </div>

        {/* Fields */}
        <div className="px-5 py-3 grid grid-cols-2 gap-x-6 gap-y-1 text-sm border-t border-border-light">
          <Field label="Baureihe" value={item.baureihe} />
          <Field label="Ebene 2" value={item.ebene2} />
          <Field label="Lagertyp" value={item.lagertyp} />
          <Field label="Rendering" value={item.medias_rendering?.toString() ?? '—'} />
          <Field label="Components" value={String(item.components?.length ?? 0)} />
          <Field label="PIM-ID" value={item.pim_id} />
        </div>

        {/* Actions */}
        {hasStep && (
          <div className="px-5 py-3 border-t border-border-light flex items-center gap-3">
            <button
              onClick={() => rerenderMut.mutate()}
              disabled={rerenderMut.isPending}
              className="btn-secondary text-sm"
            >
              <RotateCcw size={14} className={rerenderMut.isPending ? 'animate-spin' : ''} />
              {rerenderMut.isPending ? 'Queuing…' : 'Re-render'}
            </button>
            {isDraft && (
              <button
                onClick={() => { if (confirm('Remove this STEP file?')) unlinkMut.mutate() }}
                disabled={unlinkMut.isPending}
                className="text-sm text-red-500 hover:text-red-700 flex items-center gap-1 ml-auto"
              >
                <Unlink size={14} />
                {unlinkMut.isPending ? 'Removing…' : 'Remove STEP'}
              </button>
            )}
          </div>
        )}
      </div>
    </div>
  )
}
