// frontend/src/pages/crm/comms/helpdesk/TicketsTab.jsx
// Support Tickets — sidebar list + conversation thread view

import { useState, useEffect, useCallback, useRef } from 'react'
import api from '@/lib/api'
import { useAuth } from '@/hooks/useAuth'
import { useToast } from '@/components/ui/Toast'
import usePreferences from '@/hooks/usePreferences'
import Button from '@/components/ui/Button'
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 CreateTicketModal from '@/modals/crm/helpdesk/CreateTicketModal'
import { fmtRelative, fmtDateTime } from '@/lib/formatters'

// ─── Constants ────────────────────────────────────────────────────────────────

const TICKET_STATUS_META = {
  open:                 { label: 'Open',               color: 'var(--color-danger)',  bg: 'var(--color-danger-bg)'  },
  waiting_on_customer:  { label: 'Waiting on Customer', color: 'var(--color-warning)', bg: 'var(--color-warning-bg)' },
  waiting_on_staff:     { label: 'Waiting on Staff',    color: 'var(--color-info)',    bg: 'var(--color-info-bg)'    },
  resolved:             { label: 'Resolved',            color: 'var(--color-success)', bg: 'var(--color-success-bg)' },
  closed:               { label: 'Closed',              color: 'var(--color-text-muted)', bg: 'var(--color-bg-elevated)' },
}

const PRIORITY_META = {
  low:    { label: 'Low',    color: 'var(--color-info)'    },
  medium: { label: 'Medium', color: 'var(--color-warning)' },
  high:   { label: 'High',   color: 'var(--color-danger)'  },
  urgent: { label: 'Urgent', color: 'var(--color-danger)'  },
}

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

function TicketStatusPill({ status, small }) {
  const m = TICKET_STATUS_META[status] || { label: status, color: 'var(--color-text-muted)', bg: 'var(--color-bg-elevated)' }
  return (
    <span style={{
      display: 'inline-flex', alignItems: 'center', gap: 5,
      padding: small ? '2px 8px' : '3px 10px',
      borderRadius: 'var(--radius-full)',
      backgroundColor: m.bg,
      border: `1px solid ${m.color}33`,
      fontSize: small ? 10 : 'var(--font-size-xs)',
      fontWeight: 'var(--font-weight-semibold)',
      color: m.color,
      whiteSpace: 'nowrap',
      textTransform: small ? 'uppercase' : 'none',
      letterSpacing: small ? '0.04em' : 0,
    }}>
      <span style={{ width: 5, height: 5, borderRadius: '50%', backgroundColor: m.color, flexShrink: 0 }} />
      {m.label}
    </span>
  )
}

function PriorityPill({ priority }) {
  if (!priority) return null
  const m = PRIORITY_META[priority] || { label: priority, color: 'var(--color-text-muted)' }
  return (
    <span style={{
      padding: '1px 7px',
      borderRadius: 'var(--radius-full)',
      backgroundColor: `${m.color}18`,
      border: `1px solid ${m.color}33`,
      fontSize: 10,
      fontWeight: 'var(--font-weight-bold)',
      color: m.color,
      textTransform: 'uppercase',
      letterSpacing: '0.05em',
      whiteSpace: 'nowrap',
    }}>
      {m.label}
    </span>
  )
}

// ─── Ticket list item ──────────────────────────────────────────────────────────

function TicketItem({ ticket, active, onClick }) {
  const isActive = active
  return (
    <div
      onClick={onClick}
      style={{
        padding: 'var(--space-4)',
        borderRadius: 'var(--radius-lg)',
        cursor: 'pointer',
        borderLeft: isActive ? '3px solid var(--color-primary)' : '3px solid transparent',
        backgroundColor: isActive ? 'rgba(49,53,60,0.50)' : 'transparent',
        transition: 'background 0.12s, border-color 0.12s',
        marginBottom: 2,
      }}
      onMouseEnter={e => { if (!isActive) e.currentTarget.style.backgroundColor = 'rgba(49,53,60,0.25)' }}
      onMouseLeave={e => { if (!isActive) e.currentTarget.style.backgroundColor = 'transparent' }}
    >
      <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', marginBottom: 'var(--space-1)', gap: 'var(--space-2)' }}>
        <span style={{
          fontSize: 'var(--font-size-xs)',
          fontWeight: 'var(--font-weight-bold)',
          color: isActive ? 'var(--color-primary)' : 'var(--color-text-muted)',
          letterSpacing: '0.08em',
          fontFamily: 'var(--font-family-mono)',
        }}>
          #{String(ticket.id).slice(0, 6).toUpperCase()}
        </span>
        <span style={{ fontSize: 11, color: 'var(--color-text-muted)', flexShrink: 0 }}>
          {fmtRelative(ticket.updated_at)}
        </span>
      </div>
      <p style={{
        margin: '0 0 var(--space-2)',
        fontSize: 'var(--font-size-sm)',
        fontWeight: 'var(--font-weight-medium)',
        color: isActive ? 'var(--color-text-primary)' : 'var(--color-text-secondary)',
        overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap',
      }}>
        {ticket.subject}
      </p>
      <div style={{ display: 'flex', alignItems: 'center', gap: 'var(--space-2)', flexWrap: 'wrap' }}>
        <TicketStatusPill status={ticket.status} small />
        {ticket.priority && <PriorityPill priority={ticket.priority} />}
      </div>
    </div>
  )
}

// ─── Message bubble ───────────────────────────────────────────────────────────

function MessageBubble({ msg }) {
  const fromStaff = msg.sender_type === 'staff'
  const isInternal = msg.is_internal

  return (
    <div style={{
      display: 'flex',
      flexDirection: fromStaff ? 'row-reverse' : 'row',
      gap: 'var(--space-3)',
      maxWidth: '78%',
      alignSelf: fromStaff ? 'flex-end' : 'flex-start',
    }}>
      {/* Avatar */}
      <div style={{
        width: 36, height: 36, borderRadius: 'var(--radius-full)',
        backgroundColor: fromStaff ? 'var(--color-primary-subtle)' : 'var(--color-bg-island)',
        border: fromStaff ? '1px solid rgba(192,193,255,0.25)' : '1px solid var(--color-border)',
        display: 'flex', alignItems: 'center', justifyContent: 'center',
        flexShrink: 0,
        fontSize: 'var(--font-size-xs)',
        fontWeight: 'var(--font-weight-bold)',
        color: fromStaff ? 'var(--color-primary)' : 'var(--color-text-secondary)',
      }}>
        {(msg.sender_name || '?').slice(0, 1).toUpperCase()}
      </div>

      {/* Content */}
      <div style={{ flex: 1, minWidth: 0 }}>
        <div style={{
          display: 'flex', alignItems: 'baseline', gap: 'var(--space-2)',
          flexDirection: fromStaff ? 'row-reverse' : 'row',
          marginBottom: 4,
        }}>
          <span style={{ fontSize: 'var(--font-size-sm)', fontWeight: 'var(--font-weight-semibold)', color: fromStaff ? 'var(--color-primary)' : 'var(--color-text-primary)' }}>
            {msg.sender_name || (fromStaff ? 'Staff' : 'Customer')}
          </span>
          <span style={{ fontSize: 11, color: 'var(--color-text-muted)' }}>
            {fmtDateTime(msg.created_at)}
          </span>
          {isInternal && (
            <span style={{
              fontSize: 10, fontWeight: 'var(--font-weight-bold)', color: 'var(--color-warning)',
              backgroundColor: 'var(--color-warning-bg)', border: '1px solid rgba(251,191,36,0.25)',
              borderRadius: 'var(--radius-sm)', padding: '1px 6px',
              textTransform: 'uppercase', letterSpacing: '0.05em',
            }}>
              Internal
            </span>
          )}
        </div>

        <div style={{
          padding: 'var(--space-4) var(--space-5)',
          borderRadius: fromStaff ? 'var(--radius-xl) var(--radius-xl) var(--radius-sm) var(--radius-xl)' : 'var(--radius-xl) var(--radius-xl) var(--radius-xl) var(--radius-sm)',
          backgroundColor: isInternal
            ? 'rgba(251,191,36,0.06)'
            : fromStaff
              ? 'rgba(192,193,255,0.08)'
              : 'rgba(28,32,38,0.40)',
          border: isInternal
            ? '1px solid rgba(251,191,36,0.20)'
            : fromStaff
              ? '1px solid rgba(192,193,255,0.18)'
              : '1px solid var(--color-border)',
          backdropFilter: 'var(--blur-modal)',
          WebkitBackdropFilter: 'var(--blur-modal)',
          boxShadow: fromStaff ? '0 4px 16px rgba(10,14,20,0.30)' : 'none',
        }}>
          <p style={{
            margin: 0,
            fontSize: 'var(--font-size-sm)',
            color: 'var(--color-text-primary)',
            lineHeight: 1.65,
            whiteSpace: 'pre-wrap',
            wordBreak: 'break-word',
          }}>
            {msg.body}
          </p>
        </div>
      </div>
    </div>
  )
}

// ─── Reply composer ───────────────────────────────────────────────────────────

// SVG icon helper — keeps toolbar buttons crisp
function ToolbarIcon({ d, viewBox = '0 0 24 24', size = 14, strokeWidth = 2 }) {
  return (
    <svg width={size} height={size} viewBox={viewBox} fill="none" stroke="currentColor" strokeWidth={strokeWidth} strokeLinecap="round" strokeLinejoin="round">
      {Array.isArray(d)
        ? d.map((path, i) => <path key={i} d={path} />)
        : <path d={d} />
      }
    </svg>
  )
}

function ReplyComposer({ ticketId, onSent, currentUser }) {
  const toast = useToast()
  const [body,       setBody]       = useState('')
  const [isInternal, setIsInternal] = useState(false)
  const [sending,    setSending]    = useState(false)

  const textareaRef = useRef(null)

  const insertMarkdown = (before, after = '') => {
    const ta = textareaRef.current
    if (!ta) return
    const start = ta.selectionStart
    const end   = ta.selectionEnd
    const sel   = body.slice(start, end)
    const newBody = body.slice(0, start) + before + sel + after + body.slice(end)
    setBody(newBody)
    setTimeout(() => {
      ta.focus()
      ta.setSelectionRange(start + before.length, start + before.length + sel.length)
    }, 0)
  }

  const send = async () => {
    if (!body.trim()) return
    setSending(true)
    try {
      await api.post(`/tickets/${ticketId}/messages`, {
        sender_type: 'staff',
        sender_id:   currentUser?.uid || currentUser?.id || 'staff',
        sender_name: currentUser?.displayName || currentUser?.name || 'Staff',
        body:        body.trim(),
        is_internal: isInternal,
      })
      setBody('')
      setIsInternal(false)
      onSent()
    } catch (err) {
      toast.danger('Send failed', err.message)
    } finally {
      setSending(false)
    }
  }

  // Proper SVG-based toolbar buttons
  const TOOLBAR_BTNS = [
    {
      title: 'Bold',
      action: () => insertMarkdown('**', '**'),
      icon: (
        // Bold — stroke-based B letterform
        <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round">
          <path d="M6 4h8a4 4 0 0 1 4 4 4 4 0 0 1-4 4H6z"/>
          <path d="M6 12h9a4 4 0 0 1 4 4 4 4 0 0 1-4 4H6z"/>
        </svg>
      ),
    },
    {
      title: 'Italic',
      action: () => insertMarkdown('_', '_'),
      icon: (
        // Italic — classic slanted I
        <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
          <line x1="19" y1="4" x2="10" y2="4"/>
          <line x1="14" y1="20" x2="5" y2="20"/>
          <line x1="15" y1="4" x2="9" y2="20"/>
        </svg>
      ),
    },
    {
      title: 'Inline code',
      action: () => insertMarkdown('`', '`'),
      icon: (
        // Code — angle brackets
        <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
          <polyline points="16 18 22 12 16 6"/>
          <polyline points="8 6 2 12 8 18"/>
        </svg>
      ),
    },
    {
      title: 'Code block',
      action: () => insertMarkdown('```\n', '\n```'),
      icon: (
        // Code block — brackets + underline bar
        <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
          <polyline points="16 18 22 12 16 6"/>
          <polyline points="8 6 2 12 8 18"/>
          <line x1="5" y1="21" x2="19" y2="21"/>
        </svg>
      ),
    },
    {
      title: 'Bullet list',
      action: () => insertMarkdown('\n- '),
      icon: (
        // Unordered list
        <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
          <line x1="9" y1="6"  x2="20" y2="6"/>
          <line x1="9" y1="12" x2="20" y2="12"/>
          <line x1="9" y1="18" x2="20" y2="18"/>
          <circle cx="4" cy="6"  r="1.5" fill="currentColor" stroke="none"/>
          <circle cx="4" cy="12" r="1.5" fill="currentColor" stroke="none"/>
          <circle cx="4" cy="18" r="1.5" fill="currentColor" stroke="none"/>
        </svg>
      ),
    },
    {
      title: 'Numbered list',
      action: () => insertMarkdown('\n1. '),
      icon: (
        // Ordered list — "1 2 3" on left, lines on right
        <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
          <line x1="10" y1="6"  x2="21" y2="6"/>
          <line x1="10" y1="12" x2="21" y2="12"/>
          <line x1="10" y1="18" x2="21" y2="18"/>
          <path d="M4 6h1v4" strokeLinecap="round"/>
          <path d="M4 10h2"/>
          <path d="M6 14H4c0-1 2-2 2-3s-1-1.5-2-1"/>
          <path d="M4 19h2M4 22h2" strokeLinecap="round"/>
        </svg>
      ),
    },
    {
      title: 'Link',
      action: () => insertMarkdown('[', '](url)'),
      icon: (
        // Link — chain icon
        <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
          <path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/>
          <path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/>
        </svg>
      ),
    },
  ]

  return (
    <div style={{
      margin: 'var(--space-6)',
      borderRadius: 'var(--radius-xl)',
      backgroundColor: 'rgba(28,32,38,0.40)',
      backdropFilter: 'var(--blur-modal)',
      WebkitBackdropFilter: 'var(--blur-modal)',
      border: '1px solid var(--color-border)',
      overflow: 'hidden',
      boxShadow: 'var(--shadow-md)',
    }}>
      {/* Toolbar */}
      <div style={{
        display: 'flex', alignItems: 'center', gap: 2,
        padding: 'var(--space-2) var(--space-3)',
        borderBottom: '1px solid var(--color-border)',
        backgroundColor: 'rgba(28,32,38,0.40)',
      }}>
        {TOOLBAR_BTNS.map(btn => (
          <button
            key={btn.title}
            title={btn.title}
            onClick={btn.action}
            style={{
              width: 28, height: 28,
              display: 'flex', alignItems: 'center', justifyContent: 'center',
              borderRadius: 'var(--radius-md)',
              border: 'none',
              backgroundColor: 'transparent',
              color: 'var(--color-text-muted)',
              cursor: 'pointer',
              transition: 'background 0.1s, color 0.1s',
            }}
            onMouseEnter={e => { e.currentTarget.style.backgroundColor = 'var(--color-bg-island)'; e.currentTarget.style.color = 'var(--color-text-primary)' }}
            onMouseLeave={e => { e.currentTarget.style.backgroundColor = 'transparent'; e.currentTarget.style.color = 'var(--color-text-muted)' }}
          >
            {btn.icon}
          </button>
        ))}
        <div style={{ width: 1, height: 16, backgroundColor: 'var(--color-border-strong)', margin: '0 var(--space-1)' }} />
        {/* File attach — visual only for now */}
        <button
          title="Attach file"
          style={{
            width: 28, height: 28,
            display: 'flex', alignItems: 'center', justifyContent: 'center',
            borderRadius: 'var(--radius-md)', border: 'none',
            backgroundColor: 'transparent', color: 'var(--color-text-muted)',
            cursor: 'pointer',
          }}
          onMouseEnter={e => { e.currentTarget.style.backgroundColor = 'var(--color-bg-island)'; e.currentTarget.style.color = 'var(--color-text-primary)' }}
          onMouseLeave={e => { e.currentTarget.style.backgroundColor = 'transparent'; e.currentTarget.style.color = 'var(--color-text-muted)' }}
        >
          {/* Paperclip */}
          <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
            <path d="M21.44 11.05l-9.19 9.19a6 6 0 0 1-8.49-8.49l9.19-9.19a4 4 0 0 1 5.66 5.66l-9.2 9.19a2 2 0 0 1-2.83-2.83l8.49-8.48"/>
          </svg>
        </button>
      </div>

      {/* Textarea */}
      <textarea
        ref={textareaRef}
        value={body}
        onChange={e => setBody(e.target.value)}
        onKeyDown={e => { if (e.key === 'Enter' && (e.ctrlKey || e.metaKey)) send() }}
        placeholder={isInternal ? 'Write an internal note (not visible to customer)…' : 'Write a reply…'}
        rows={4}
        style={{
          display: 'block', width: '100%', boxSizing: 'border-box',
          padding: 'var(--space-5)',
          backgroundColor: 'transparent',
          border: 'none', outline: 'none', resize: 'vertical',
          fontSize: 'var(--font-size-sm)',
          color: 'var(--color-text-primary)',
          fontFamily: 'var(--font-family-base)',
          lineHeight: 1.65,
        }}
      />

      {/* Action bar */}
      <div style={{
        display: 'flex', alignItems: 'center', justifyContent: 'space-between',
        padding: 'var(--space-3) var(--space-4)',
        borderTop: '1px solid var(--color-border)',
        backgroundColor: 'rgba(16,20,26,0.30)',
      }}>
        {/* Internal toggle */}
        <button
          onClick={() => setIsInternal(p => !p)}
          style={{
            display: 'flex', alignItems: 'center', gap: 'var(--space-2)',
            padding: '4px 10px',
            borderRadius: 'var(--radius-md)',
            border: `1px solid ${isInternal ? 'rgba(251,191,36,0.35)' : 'var(--color-border)'}`,
            backgroundColor: isInternal ? 'rgba(251,191,36,0.08)' : 'transparent',
            color: isInternal ? 'var(--color-warning)' : 'var(--color-text-muted)',
            fontSize: 'var(--font-size-xs)',
            fontWeight: 'var(--font-weight-medium)',
            cursor: 'pointer',
            transition: 'all 0.15s',
          }}
        >
          {/* Lock icon */}
          <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
            <rect x="3" y="11" width="18" height="11" rx="2" ry="2"/>
            <path d="M7 11V7a5 5 0 0 1 10 0v4"/>
          </svg>
          {isInternal ? 'Internal note' : 'Mark as internal'}
        </button>

        <div style={{ display: 'flex', alignItems: 'center', gap: 'var(--space-3)' }}>
          <span style={{ fontSize: 11, color: 'var(--color-text-muted)' }}>Ctrl+Enter to send</span>
          <Button variant="primary" size="sm" onClick={send} loading={sending} disabled={!body.trim()}>
            Send Reply
          </Button>
        </div>
      </div>
    </div>
  )
}

// ─── Thread view ──────────────────────────────────────────────────────────────

function TicketThread({ ticket, onReload, currentUser }) {
  const toast = useToast()
  const [updating, setUpdating] = useState(false)
  const messagesEndRef = useRef(null)

  useEffect(() => {
    messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' })
  }, [ticket?.messages?.length])

  const changeStatus = async (newStatus) => {
    setUpdating(true)
    try {
      await api.patch(`/tickets/${ticket.id}`, { status: newStatus })
      onReload()
    } catch (err) {
      toast.danger('Update failed', err.message)
    } finally {
      setUpdating(false)
    }
  }

  if (!ticket) return (
    <div style={{ flex: 1, display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', color: 'var(--color-text-muted)', gap: 'var(--space-3)' }}>
      <svg width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1" strokeLinecap="round" strokeLinejoin="round" style={{ opacity: 0.25 }}>
        <path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/>
      </svg>
      <p style={{ margin: 0, fontSize: 'var(--font-size-base)' }}>Select a ticket to view the conversation</p>
    </div>
  )

  const messages = ticket.messages || []

  return (
    <div style={{ flex: 1, display: 'flex', flexDirection: 'column', overflow: 'hidden' }}>
      {/* Thread header */}
      <div style={{
        padding: 'var(--space-4) var(--space-6)',
        borderBottom: '1px solid var(--color-border)',
        backgroundColor: 'rgba(28,32,38,0.40)',
        backdropFilter: 'var(--blur-modal)',
        WebkitBackdropFilter: 'var(--blur-modal)',
        display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 'var(--space-4)',
        flexShrink: 0,
      }}>
        <div style={{ minWidth: 0 }}>
          <h3 style={{ margin: 0, fontSize: 'var(--font-size-md)', fontWeight: 'var(--font-weight-semibold)', color: 'var(--color-text-primary)', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
            {ticket.subject}
          </h3>
          <p style={{ margin: '2px 0 0', fontSize: 'var(--font-size-xs)', color: 'var(--color-text-muted)' }}>
            {ticket.customer_name || 'Unknown customer'}
            {ticket.device_serial ? ` · Device ${ticket.device_serial}` : ''}
            {' · '}{messages.length} message{messages.length !== 1 ? 's' : ''}
          </p>
        </div>

        <div style={{ display: 'flex', alignItems: 'center', gap: 'var(--space-2)', flexShrink: 0 }}>
          <TicketStatusPill status={ticket.status} />
          {ticket.priority && <PriorityPill priority={ticket.priority} />}

          {ticket.status !== 'resolved' && (
            <Button variant="success" size="sm" onClick={() => changeStatus('resolved')} loading={updating}>
              Mark Resolved
            </Button>
          )}
          {ticket.status !== 'closed' && ticket.status === 'resolved' && (
            <Button variant="secondary" size="sm" onClick={() => changeStatus('closed')} loading={updating}>
              Close
            </Button>
          )}
          {ticket.status === 'closed' && (
            <Button variant="secondary" size="sm" onClick={() => changeStatus('open')} loading={updating}>
              Reopen
            </Button>
          )}
        </div>
      </div>

      {/* Messages */}
      <div style={{ flex: 1, overflowY: 'auto', padding: 'var(--space-6)', display: 'flex', flexDirection: 'column', gap: 'var(--space-6)' }}>
        {messages.length === 0 ? (
          <div style={{ textAlign: 'center', color: 'var(--color-text-muted)', fontSize: 'var(--font-size-sm)', paddingTop: 'var(--space-10)' }}>
            No messages yet. Send the first reply below.
          </div>
        ) : (
          messages.map(msg => (
            <MessageBubble key={msg.id} msg={msg} />
          ))
        )}
        <div ref={messagesEndRef} />
      </div>

      {/* Reply composer */}
      <div style={{ flexShrink: 0 }}>
        <ReplyComposer ticketId={ticket.id} onSent={onReload} currentUser={currentUser} />
      </div>
    </div>
  )
}

// ─── TicketsTab ────────────────────────────────────────────────────────────────

export default function TicketsTab({ onOpenCreate }) {
  const { user } = useAuth()
  const toast = useToast()

  const { prefs, setPref, setPrefBatch, ready } = usePreferences('helpdesk_tickets')

  const [tickets,      setTickets]      = useState([])
  const [selected,     setSelected]     = useState(null)
  const [loading,      setLoading]      = useState(true)
  const [error,        setError]        = useState('')
  const [search,       setSearch]       = useState('')
  const [statusFilter, setStatusFilter] = useState('open')
  const [createOpen,   setCreateOpen]   = useState(false)

  // When parent increments onOpenCreate, open the create modal
  useEffect(() => {
    if (onOpenCreate && onOpenCreate > 0) setCreateOpen(true)
  // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [onOpenCreate])

  const [prefsLoaded, setPrefsLoaded] = useState(false)

  // ── Load preferences once ready ──
  useEffect(() => {
    if (!ready || prefsLoaded) return
    if (prefs.status_filter !== undefined) setStatusFilter(prefs.status_filter)
    setPrefsLoaded(true)
  }, [ready, prefs, prefsLoaded])

  // ── Persist filter changes ──
  useEffect(() => {
    if (!prefsLoaded) return
    setPref('status_filter', statusFilter)
  }, [statusFilter]) // eslint-disable-line react-hooks/exhaustive-deps

  const loadTickets = useCallback(async () => {
    setLoading(true)
    setError('')
    try {
      const params = new URLSearchParams({ limit: 100 })
      if (statusFilter) params.set('status', statusFilter)
      const data = await api.get(`/tickets?${params}`)
      setTickets(data.data || [])
    } catch (err) {
      setError(err.message || 'Failed to load tickets')
    } finally {
      setLoading(false)
    }
  }, [statusFilter])

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

  const reloadSelected = useCallback(async () => {
    await loadTickets()
    if (selected) {
      try {
        const fresh = await api.get(`/tickets/${selected.id}`)
        setSelected(fresh)
      } catch { /* silent */ }
    }
  }, [loadTickets, selected])

  const selectTicket = async (t) => {
    try {
      const full = await api.get(`/tickets/${t.id}`)
      setSelected(full)
    } catch (err) {
      toast.danger('Failed to load ticket', err.message)
    }
  }

  const q = search.trim().toLowerCase()
  const filtered = q
    ? tickets.filter(t =>
        t.subject.toLowerCase().includes(q) ||
        (t.customer_name || '').toLowerCase().includes(q) ||
        (t.device_serial || '').toLowerCase().includes(q)
      )
    : tickets

  return (
    <>
      <style>{`
        .tickets-layout {
          display: flex;
          height: calc(100vh - 280px);
          min-height: 500px;
          border-radius: var(--radius-xl);
          border: 1px solid var(--color-border);
          overflow: hidden;
          background: rgba(28,32,38,0.30);
          backdrop-filter: var(--blur-modal);
          -webkit-backdrop-filter: var(--blur-modal);
          box-shadow: var(--shadow-card);
        }
      `}</style>

      <div className="tickets-layout">
        {/* ── Sidebar ── */}
        <aside style={{
          width: 320,
          flexShrink: 0,
          display: 'flex',
          flexDirection: 'column',
          borderRight: '1px solid var(--color-border)',
          backgroundColor: 'rgba(16,20,26,0.30)',
        }}>
          {/* Sidebar header */}
          <div style={{ padding: 'var(--space-4)', borderBottom: '1px solid var(--color-border)', display: 'flex', flexDirection: 'column', gap: 'var(--space-3)' }}>
            <SearchBar value={search} onChange={setSearch} placeholder="Search tickets…" />
            <div>
              <Select value={statusFilter} onChange={e => setStatusFilter(e.target.value)}>
                <option value="">All statuses</option>
                {Object.entries(TICKET_STATUS_META).map(([k, v]) => (
                  <option key={k} value={k}>{v.label}</option>
                ))}
              </Select>
            </div>
          </div>

          {/* Ticket list */}
          <div style={{ flex: 1, overflowY: 'auto', padding: 'var(--space-2)' }}>
            {loading ? (
              <div style={{ display: 'flex', justifyContent: 'center', padding: 'var(--space-8)' }}>
                <Spinner size="sm" />
              </div>
            ) : error ? (
              <div style={{ padding: 'var(--space-4)', color: 'var(--color-danger)', fontSize: 'var(--font-size-xs)' }}>{error}</div>
            ) : filtered.length === 0 ? (
              <div style={{ padding: 'var(--space-8)', textAlign: 'center', color: 'var(--color-text-muted)', fontSize: 'var(--font-size-sm)' }}>
                {search ? 'No tickets match your search.' : 'No tickets found.'}
              </div>
            ) : (
              filtered.map(t => (
                <TicketItem
                  key={t.id}
                  ticket={t}
                  active={selected?.id === t.id}
                  onClick={() => selectTicket(t)}
                />
              ))
            )}
          </div>

          {/* Sidebar footer */}
          {!loading && (
            <div style={{ padding: 'var(--space-3) var(--space-4)', borderTop: '1px solid var(--color-border)', fontSize: 11, color: 'var(--color-text-muted)' }}>
              {filtered.length} ticket{filtered.length !== 1 ? 's' : ''}
            </div>
          )}
        </aside>

        {/* ── Thread view ── */}
        <TicketThread
          ticket={selected}
          onReload={reloadSelected}
          currentUser={user}
        />
      </div>

      <CreateTicketModal
        open={createOpen}
        onClose={() => setCreateOpen(false)}
        onCreated={(ticket) => { setCreateOpen(false); loadTickets(); selectTicket(ticket) }}
      />
    </>
  )
}
