// frontend/src/pages/crm/customers/tabs/QuotationsTab.jsx
// Customer-scoped quotation list — fetches from /crm/quotations/customer/{id}
// Reuses the same PDF thumbnail + row design as the global QuotationList page.

import { useState, useEffect, useCallback, useRef } from 'react'
import { useNavigate } from 'react-router-dom'
import api from '@/lib/api'
import Button from '@/components/ui/Button'
import StatusBadge from '@/components/ui/StatusBadge'
import Spinner from '@/components/ui/Spinner'
import SearchBar from '@/components/ui/SearchBar'
import Select from '@/components/ui/Select'
import ConfirmDialog from '@/components/ui/ConfirmDialog'
import Icon from '@/components/ui/Icon'
import PdfViewModal from '@/modals/crm/PdfViewModal'
import ComposeEmailModal from '@/modals/crm/ComposeEmailModal'
import { useToast } from '@/components/ui/Toast'
import { fmtDate as fmtDateCentral, fmtEuro } from '@/lib/formatters'

// ─── Helpers ──────────────────────────────────────────────────────────────────

const STATUS_BADGE_MAP = { draft: 'neutral', built: 'warning', sent: 'info', accepted: 'success', declined: 'danger' }
const STATUS_OPTIONS = ['draft', 'built', 'sent', 'accepted', 'declined']

function fmtDate(iso) { return fmtDateCentral(iso) }
function fmt(n) { return fmtEuro(n) }

// ─── PDF thumbnail (via PDF.js) ───────────────────────────────────────────────

const THUMB_W = 60, THUMB_H = 78

function loadPdfJs() {
  return new Promise((res, rej) => {
    if (window.pdfjsLib) { res(); return }
    if (document.getElementById('__pdfjs__')) {
      const check = setInterval(() => { if (window.pdfjsLib) { clearInterval(check); res() } }, 50)
      return
    }
    const s = document.createElement('script')
    s.id = '__pdfjs__'
    s.src = 'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf.min.js'
    s.onload = () => { window.pdfjsLib.GlobalWorkerOptions.workerSrc = 'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf.worker.min.js'; res() }
    s.onerror = rej
    document.head.appendChild(s)
  })
}

function PdfThumbnail({ quotationId, onClick }) {
  const canvasRef = useRef(null)
  const [failed, setFailed] = useState(false)

  useEffect(() => {
    let cancelled = false
    ;(async () => {
      try {
        await loadPdfJs()
        const token = localStorage.getItem('access_token')
        const loadingTask = window.pdfjsLib.getDocument({
          url: `/api/crm/quotations/${quotationId}/pdf`,
          httpHeaders: token ? { Authorization: `Bearer ${token}` } : {},
        })
        const pdf = await loadingTask.promise
        if (cancelled) return
        const page = await pdf.getPage(1)
        if (cancelled) return
        const canvas = canvasRef.current
        if (!canvas) return
        const vp = page.getViewport({ scale: 1 })
        const scale = Math.min(THUMB_W / vp.width, THUMB_H / vp.height)
        const scaled = page.getViewport({ scale })
        canvas.width = scaled.width
        canvas.height = scaled.height
        await page.render({ canvasContext: canvas.getContext('2d'), viewport: scaled }).promise
      } catch { if (!cancelled) setFailed(true) }
    })()
    return () => { cancelled = true }
  }, [quotationId])

  const baseStyle = {
    width: THUMB_W, height: THUMB_H, borderRadius: 'var(--radius-sm)', overflow: 'hidden',
    cursor: 'pointer', border: '1px solid var(--color-border)', display: 'flex',
    alignItems: 'center', justifyContent: 'center', backgroundColor: 'var(--color-bg-abyss)',
    flexShrink: 0, transition: 'border-color 0.15s, box-shadow 0.15s',
  }

  return (
    <div
      onClick={onClick}
      title="Preview PDF"
      style={baseStyle}
      onMouseEnter={e => { e.currentTarget.style.borderColor = 'var(--color-primary)'; e.currentTarget.style.boxShadow = 'var(--shadow-focus)' }}
      onMouseLeave={e => { e.currentTarget.style.borderColor = 'var(--color-border)'; e.currentTarget.style.boxShadow = 'none' }}
    >
      {failed ? (
        <svg width="24" height="30" viewBox="0 0 16 20" fill="none">
          <path d="M2 1h9l4 4v14H2V1z" stroke="var(--color-text-muted)" strokeWidth="1.2" strokeLinejoin="round" />
          <path d="M9 1v4h4" stroke="var(--color-text-muted)" strokeWidth="1.2" />
          <path d="M4 9h8M4 12h6M4 15h4" stroke="var(--color-text-muted)" strokeWidth="1.2" strokeLinecap="round" />
        </svg>
      ) : (
        <canvas ref={canvasRef} style={{ width: '100%', height: '100%', objectFit: 'contain', display: 'block' }} />
      )}
    </div>
  )
}

function DraftThumbnail() {
  return (
    <div style={{
      width: THUMB_W, height: THUMB_H, borderRadius: 'var(--radius-sm)', flexShrink: 0,
      border: '1px dashed var(--color-border)', display: 'flex', flexDirection: 'column',
      alignItems: 'center', justifyContent: 'center', backgroundColor: 'var(--color-bg-abyss)', gap: 4,
    }}>
      <svg width="20" height="26" viewBox="0 0 16 20" fill="none">
        <path d="M2 1h9l4 4v14H2V1z" stroke="var(--color-text-muted)" strokeWidth="1.2" strokeLinejoin="round" />
        <path d="M9 1v4h4" stroke="var(--color-text-muted)" strokeWidth="1.2" />
        <path d="M4 9h8M4 12h5" stroke="var(--color-border-strong)" strokeWidth="1.2" strokeLinecap="round" />
      </svg>
      <span style={{ fontSize: 7, fontWeight: 700, color: 'var(--color-text-muted)', letterSpacing: '0.08em', textTransform: 'uppercase', fontFamily: 'var(--font-family-mono)' }}>DRAFT</span>
    </div>
  )
}

// ─── Actions dropdown ─────────────────────────────────────────────────────────

function ActionsMenu({ quotation, onSend, onEdit, onCopy, onDelete, onStatusChange, deleting }) {
  const [open, setOpen] = useState(false)
  const [statusOpen, setStatusOpen] = useState(false)
  const [menuPos, setMenuPos] = useState({ top: 0, right: 0 })
  const ref = useRef(null)
  const btnRef = useRef(null)

  useEffect(() => {
    if (!open) return
    const h = e => { if (ref.current && !ref.current.contains(e.target)) { setOpen(false); setStatusOpen(false) } }
    document.addEventListener('mousedown', h)
    return () => document.removeEventListener('mousedown', h)
  }, [open])

  const openMenu = (e) => {
    e.stopPropagation()
    if (!open && btnRef.current) {
      const r = btnRef.current.getBoundingClientRect()
      setMenuPos({ top: r.bottom + 4, right: window.innerWidth - r.right })
    }
    setOpen(o => !o)
    setStatusOpen(false)
  }

  const item = (label, icon, onClick, danger = false) => (
    <button
      onClick={e => { e.stopPropagation(); setOpen(false); setStatusOpen(false); onClick() }}
      style={{
        width: '100%', display: 'flex', alignItems: 'center', gap: 'var(--space-2)',
        padding: 'var(--space-2) var(--space-3)', backgroundColor: 'transparent', border: 'none',
        cursor: 'pointer', fontSize: 'var(--font-size-sm)', textAlign: 'left',
        color: danger ? 'var(--color-danger)' : 'var(--color-text-secondary)',
        transition: 'background-color 0.1s',
      }}
      onMouseEnter={e => e.currentTarget.style.backgroundColor = danger ? 'var(--color-danger-bg)' : 'var(--color-bg-elevated)'}
      onMouseLeave={e => e.currentTarget.style.backgroundColor = 'transparent'}
    >
      <Icon name={icon} size={13} />
      {label}
    </button>
  )

  return (
    <div ref={ref}>
      <button
        ref={btnRef}
        onClick={openMenu}
        style={{
          display: 'flex', alignItems: 'center', gap: 4, padding: '0 var(--space-3)', height: 28,
          borderRadius: 'var(--radius-md)', border: '1px solid var(--color-border)',
          backgroundColor: open ? 'var(--color-bg-elevated)' : 'transparent',
          color: 'var(--color-text-secondary)', fontSize: 'var(--font-size-sm)',
          fontWeight: 'var(--font-weight-medium)', cursor: 'pointer', whiteSpace: 'nowrap',
          transition: 'border-color 0.15s, background-color 0.15s',
        }}
        onMouseEnter={e => { e.currentTarget.style.borderColor = 'var(--color-border-strong)'; e.currentTarget.style.backgroundColor = 'var(--color-bg-elevated)' }}
        onMouseLeave={e => { if (!open) { e.currentTarget.style.borderColor = 'var(--color-border)'; e.currentTarget.style.backgroundColor = 'transparent' } }}
      >
        Actions
        <svg width="10" height="10" viewBox="0 0 10 10" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round">
          <path d="M2 3.5l3 3 3-3" />
        </svg>
      </button>

      {open && (
        <div style={{
          position: 'fixed', top: menuPos.top, right: menuPos.right, zIndex: 9999, minWidth: 170,
          backgroundColor: 'var(--color-bg-float)', backdropFilter: 'blur(12px)',
          border: '1px solid var(--color-border)', borderRadius: 'var(--radius-md)',
          boxShadow: 'var(--shadow-lg)', overflow: 'hidden',
        }}>
          {item('Send', 'mail', onSend)}
          {item('Edit', 'edit', onEdit)}
          {item('Copy', 'copy', onCopy)}

          {/* Status sub-menu toggle */}
          <button
            onClick={e => { e.stopPropagation(); setStatusOpen(o => !o) }}
            style={{
              width: '100%', display: 'flex', alignItems: 'center', justifyContent: 'space-between',
              padding: 'var(--space-2) var(--space-3)', backgroundColor: statusOpen ? 'var(--color-bg-elevated)' : 'transparent',
              border: 'none', cursor: 'pointer', fontSize: 'var(--font-size-sm)', color: 'var(--color-text-secondary)',
              transition: 'background-color 0.1s',
            }}
            onMouseEnter={e => e.currentTarget.style.backgroundColor = 'var(--color-bg-elevated)'}
            onMouseLeave={e => { if (!statusOpen) e.currentTarget.style.backgroundColor = 'transparent' }}
          >
            <span style={{ display: 'flex', alignItems: 'center', gap: 'var(--space-2)' }}>
              <Icon name="edit" size={13} />
              Change Status
            </span>
            <svg width="10" height="10" viewBox="0 0 10 10" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round" style={{ transform: statusOpen ? 'rotate(180deg)' : 'none', transition: 'transform 0.15s' }}>
              <path d="M2 3.5l3 3 3-3" />
            </svg>
          </button>

          {statusOpen && (
            <div style={{ borderTop: '1px solid var(--color-border)', backgroundColor: 'var(--color-bg-abyss)' }}>
              {STATUS_OPTIONS.map(s => (
                <button
                  key={s}
                  onClick={e => { e.stopPropagation(); setOpen(false); setStatusOpen(false); onStatusChange(s) }}
                  disabled={quotation.status === s}
                  style={{
                    width: '100%', display: 'flex', alignItems: 'center', gap: 'var(--space-2)',
                    padding: 'var(--space-2) var(--space-4)', backgroundColor: 'transparent', border: 'none',
                    cursor: quotation.status === s ? 'default' : 'pointer', fontSize: 'var(--font-size-sm)',
                    color: quotation.status === s ? 'var(--color-text-muted)' : 'var(--color-text-secondary)',
                    opacity: quotation.status === s ? 0.5 : 1, textAlign: 'left', transition: 'background-color 0.1s',
                  }}
                  onMouseEnter={e => { if (quotation.status !== s) e.currentTarget.style.backgroundColor = 'var(--color-bg-elevated)' }}
                  onMouseLeave={e => e.currentTarget.style.backgroundColor = 'transparent'}
                >
                  {s.charAt(0).toUpperCase() + s.slice(1)}
                </button>
              ))}
            </div>
          )}

          <div style={{ height: 1, backgroundColor: 'var(--color-border)', margin: 'var(--space-1) 0' }} />
          <button
            onClick={e => { e.stopPropagation(); setOpen(false); setStatusOpen(false); onDelete(e) }}
            disabled={deleting}
            style={{
              width: '100%', display: 'flex', alignItems: 'center', gap: 'var(--space-2)',
              padding: 'var(--space-2) var(--space-3)', backgroundColor: 'transparent', border: 'none',
              cursor: deleting ? 'not-allowed' : 'pointer', fontSize: 'var(--font-size-sm)',
              color: 'var(--color-danger)', textAlign: 'left', opacity: deleting ? 0.5 : 1,
              transition: 'background-color 0.1s',
            }}
            onMouseEnter={e => e.currentTarget.style.backgroundColor = 'var(--color-danger-bg)'}
            onMouseLeave={e => e.currentTarget.style.backgroundColor = 'transparent'}
          >
            <Icon name="delete" size={13} />
            Delete
          </button>
        </div>
      )}
    </div>
  )
}

// ─── Main component ───────────────────────────────────────────────────────────

export default function QuotationsTab({ customer, canEdit }) {
  const navigate = useNavigate()
  const { toast } = useToast()

  const [quotations, setQuotations] = useState([])
  const [loading, setLoading]       = useState(true)
  const [error, setError]           = useState(null)
  const [search, setSearch]         = useState('')
  const [filterStatus, setFilterStatus] = useState('')
  const [pdfPreview, setPdfPreview]     = useState(null)
  const [deleting, setDeleting]         = useState(null)
  const [deleteTarget, setDeleteTarget] = useState(null)
  const [composeTarget, setComposeTarget] = useState(null) // { quotation, defaultTo, attachments: File[] }

  const load = useCallback(async () => {
    setLoading(true)
    setError(null)
    try {
      const res = await api.get(`/crm/quotations/customer/${customer.id}`)
      setQuotations(Array.isArray(res) ? res : (res.quotations || []))
    } catch {
      setError('Failed to load quotations.')
      setQuotations([])
    } finally {
      setLoading(false)
    }
  }, [customer.id])

  useEffect(() => { load() }, [load])

  const handleDelete = async () => {
    if (!deleteTarget) return
    setDeleting(deleteTarget.id)
    try {
      await api.delete(`/crm/quotations/${deleteTarget.id}`)
      toast.success('Deleted', `Quotation ${deleteTarget.quotation_number} removed.`)
      setQuotations(prev => prev.filter(x => x.id !== deleteTarget.id))
      setDeleteTarget(null)
    } catch {
      toast.danger('Error', 'Failed to delete quotation.')
    } finally {
      setDeleting(null)
    }
  }

  // Navigate to global quotation form pre-scoped to this customer
  const handleNew = () => navigate(`/crm/quotations/new?customer_id=${customer.id}`)

  const handleSend = async (q) => {
    const customerEmail = (customer.contacts || []).find(c => c.type === 'email')?.value || ''
    const hasPdf = q.is_legacy ? !!q.legacy_pdf_path : !!q.nextcloud_pdf_url
    let attachments = []
    if (hasPdf) {
      try {
        const token = localStorage.getItem('access_token')
        const resp = await fetch(`/api/crm/quotations/${q.id}/pdf`, {
          headers: { Authorization: `Bearer ${token}` },
        })
        if (resp.ok) {
          const blob = await resp.blob()
          attachments = [new File([blob], `${q.quotation_number}.pdf`, { type: 'application/pdf' })]
        }
      } catch { /* open without attachment if fetch fails */ }
    }
    setComposeTarget({ quotation: q, defaultTo: customerEmail, attachments })
  }

  const handleStatusChange = async (q, newStatus) => {
    try {
      await api.put(`/crm/quotations/${q.id}`, { status: newStatus })
      setQuotations(prev => prev.map(x => x.id === q.id ? { ...x, status: newStatus } : x))
      toast.success('Status updated', `Quotation ${q.quotation_number} marked as ${newStatus}.`)
    } catch {
      toast.danger('Error', 'Failed to update status.')
    }
  }

  const handleEdit = (q) => navigate(`/crm/quotations/${q.id}`)

  const handleCopy = (q) => navigate(`/crm/quotations/new?copy_from=${q.id}&customer_id=${customer.id}`)

  const filtered = quotations.filter(q => {
    if (filterStatus && q.status !== filterStatus) return false
    if (search.trim()) {
      const s = search.toLowerCase()
      if (
        !(q.quotation_number || '').toLowerCase().includes(s) &&
        !(q.title || '').toLowerCase().includes(s)
      ) return false
    }
    return true
  })

  return (
    <div style={{ display: 'flex', flexDirection: 'column', gap: 'var(--space-5)' }}>
      {/* Toolbar */}
      <div style={{ display: 'flex', gap: 'var(--space-3)', alignItems: 'center', flexWrap: 'wrap' }}>
        <div style={{ flex: 1, minWidth: 200, maxWidth: 360 }}>
          <SearchBar value={search} onChange={setSearch} placeholder="Search by number or title…" />
        </div>
        <Select value={filterStatus} onChange={e => setFilterStatus(e.target.value)} style={{ width: 150 }}>
          <option value="">All statuses</option>
          {STATUS_OPTIONS.map(s => (
            <option key={s} value={s}>{s.charAt(0).toUpperCase() + s.slice(1)}</option>
          ))}
        </Select>
        {(search || filterStatus) && (
          <Button variant="ghost" size="sm" onClick={() => { setSearch(''); setFilterStatus('') }}>Clear</Button>
        )}
        {canEdit && (
          <div style={{ marginLeft: 'auto' }}>
            <Button variant="primary" size="md" onClick={handleNew}>
              + New Quotation
            </Button>
          </div>
        )}
      </div>

      {/* Loading */}
      {loading && (
        <div style={{ display: 'flex', justifyContent: 'center', padding: 'var(--space-12)' }}>
          <Spinner size="lg" />
        </div>
      )}

      {/* Error */}
      {!loading && error && (
        <div style={{ padding: 'var(--space-5)', backgroundColor: 'var(--color-danger-bg)', border: '1px solid var(--color-danger)', borderRadius: 'var(--radius-lg)', color: 'var(--color-danger)', fontSize: 'var(--font-size-sm)' }}>
          {error}
        </div>
      )}

      {/* Empty */}
      {!loading && !error && filtered.length === 0 && (
        <div style={{ padding: 'var(--space-12)', display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 'var(--space-3)', color: 'var(--color-text-muted)' }}>
          <svg width="40" height="40" viewBox="0 0 40 40" fill="none">
            <rect x="6" y="2" width="22" height="30" rx="2" stroke="var(--color-text-muted)" strokeWidth="1.5" />
            <path d="M28 2l6 6v24a2 2 0 01-2 2H6" stroke="var(--color-text-muted)" strokeWidth="1.5" strokeLinejoin="round" />
            <path d="M28 2v6h6" stroke="var(--color-text-muted)" strokeWidth="1.5" />
            <path d="M11 14h16M11 19h12M11 24h8" stroke="var(--color-border-strong)" strokeWidth="1.5" strokeLinecap="round" />
          </svg>
          <p style={{ fontSize: 'var(--font-size-base)', fontWeight: 'var(--font-weight-semibold)', color: 'var(--color-text-secondary)', margin: 0 }}>
            {quotations.length === 0 ? 'No quotations yet' : 'No results match your filters'}
          </p>
          <p style={{ fontSize: 'var(--font-size-sm)', margin: 0 }}>
            {quotations.length === 0
              ? 'Create the first quotation for this customer.'
              : 'Try adjusting your search or status filter.'}
          </p>
          {canEdit && quotations.length === 0 && (
            <Button variant="primary" size="md" onClick={handleNew} style={{ marginTop: 'var(--space-2)' }}>
              New Quotation
            </Button>
          )}
        </div>
      )}

      {/* Quotation rows */}
      {!loading && !error && filtered.length > 0 && (
        <div style={{
          backgroundColor: 'var(--color-bg-surface)',
          border: '1px solid var(--color-border)',
          borderRadius: 'var(--radius-lg)',
          overflow: 'hidden',
          boxShadow: 'var(--shadow-card)',
        }}>
          {/* Header */}
          <div style={{
            display: 'grid',
            gridTemplateColumns: `${THUMB_W}px 1fr minmax(0,2fr) 100px 110px 140px 110px`,
            gap: 'var(--space-3)',
            padding: 'var(--space-3) var(--space-5)',
            borderBottom: '1px solid var(--color-border)',
            backgroundColor: 'var(--color-bg-void)',
          }}>
            {['', 'Number', 'Title', 'Date', 'Status', 'Total', ''].map((h, i) => (
              <div key={i} style={{
                fontSize: 'var(--font-size-xs)', fontWeight: 'var(--font-weight-semibold)',
                color: 'var(--color-text-muted)', textTransform: 'uppercase',
                letterSpacing: 'var(--tracking-wide)', textAlign: i === 5 ? 'right' : 'left',
              }}>
                {h}
              </div>
            ))}
          </div>

          {/* Rows */}
          {filtered.map((q, i) => {
            const hasPdf = q.is_legacy ? !!q.legacy_pdf_path : !!q.nextcloud_pdf_url
            const displayDate = q.is_legacy
              ? (q.legacy_date ? fmtDate(q.legacy_date) : fmtDate(q.created_at))
              : fmtDate(q.created_at)

            return (
              <div
                key={q.id}
                onClick={() => navigate(`/crm/quotations/${q.id}`)}
                style={{
                  display: 'grid',
                  gridTemplateColumns: `${THUMB_W}px 1fr minmax(0,2fr) 100px 110px 140px 110px`,
                  gap: 'var(--space-3)',
                  padding: 'var(--space-4) var(--space-5)',
                  borderBottom: i < filtered.length - 1 ? '1px solid var(--color-border)' : 'none',
                  alignItems: 'center',
                  cursor: 'pointer',
                  transition: 'background-color 0.12s',
                  minHeight: 88,
                }}
                onMouseEnter={e => e.currentTarget.style.backgroundColor = 'var(--color-bg-elevated)'}
                onMouseLeave={e => e.currentTarget.style.backgroundColor = ''}
              >
                {/* Thumbnail */}
                <div onClick={e => e.stopPropagation()}>
                  {hasPdf
                    ? <PdfThumbnail quotationId={q.id} onClick={() => setPdfPreview({ id: q.id, number: q.quotation_number })} />
                    : <DraftThumbnail />}
                </div>

                {/* Number */}
                <div style={{ display: 'flex', alignItems: 'center', gap: 'var(--space-2)', overflow: 'hidden' }}>
                  <span style={{ fontFamily: 'var(--font-family-mono)', fontSize: 'var(--font-size-sm)', fontWeight: 'var(--font-weight-semibold)', color: 'var(--color-text-primary)', flexShrink: 0 }}>
                    {q.quotation_number}
                  </span>
                  {q.is_legacy && (
                    <span style={{ fontSize: 'var(--font-size-xs)', fontWeight: 700, padding: '1px 5px', borderRadius: 'var(--radius-sm)', backgroundColor: 'rgba(251,191,36,0.12)', color: 'var(--color-warning)', textTransform: 'uppercase', letterSpacing: '0.06em', flexShrink: 0 }}>
                      Legacy
                    </span>
                  )}
                </div>

                {/* Title */}
                <div style={{ overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', fontSize: 'var(--font-size-base)', color: 'var(--color-text-secondary)' }}>
                  {q.title || <span style={{ color: 'var(--color-text-muted)', fontStyle: 'italic' }}>Untitled</span>}
                </div>

                {/* Date */}
                <div style={{ fontSize: 'var(--font-size-sm)', color: 'var(--color-text-muted)', whiteSpace: 'nowrap' }}>
                  {displayDate}
                </div>

                {/* Status */}
                <div>
                  <StatusBadge variant={STATUS_BADGE_MAP[q.status] || 'neutral'}>
                    {q.status ? q.status.charAt(0).toUpperCase() + q.status.slice(1) : '—'}
                  </StatusBadge>
                </div>

                {/* Total */}
                <div style={{ textAlign: 'right', fontFamily: 'var(--font-family-mono)', fontSize: 'var(--font-size-sm)', fontWeight: 'var(--font-weight-semibold)', color: 'var(--color-text-primary)', whiteSpace: 'nowrap' }}>
                  {fmt(q.final_total)}
                </div>

                {/* Actions */}
                <div style={{ display: 'flex', justifyContent: 'flex-end' }} onClick={e => e.stopPropagation()}>
                  {canEdit && (
                    <ActionsMenu
                      quotation={q}
                      deleting={deleting === q.id}
                      onSend={() => handleSend(q)}
                      onEdit={() => handleEdit(q)}
                      onCopy={() => handleCopy(q)}
                      onDelete={() => setDeleteTarget(q)}
                      onStatusChange={s => handleStatusChange(q, s)}
                    />
                  )}
                </div>
              </div>
            )
          })}
        </div>
      )}

      {/* Compose email modal */}
      <ComposeEmailModal
        open={composeTarget !== null}
        onClose={() => setComposeTarget(null)}
        defaultTo={composeTarget?.defaultTo || ''}
        defaultSubject={composeTarget ? `Quotation ${composeTarget.quotation.quotation_number}` : ''}
        defaultAttachments={composeTarget?.attachments || []}
        customerId={customer.id}
      />

      {/* PDF preview modal */}
      <PdfViewModal
        open={pdfPreview !== null}
        quotationId={pdfPreview?.id}
        quotationNumber={pdfPreview?.number}
        onClose={() => setPdfPreview(null)}
      />

      {/* Delete confirm */}
      <ConfirmDialog
        open={deleteTarget !== null}
        variant="danger"
        title="Delete Quotation"
        message={`Delete quotation ${deleteTarget?.quotation_number}? This cannot be undone.`}
        confirmLabel={deleting ? 'Deleting…' : 'Delete'}
        onConfirm={handleDelete}
        onCancel={() => setDeleteTarget(null)}
      />
    </div>
  )
}
