// App principale: storico reale, upload audio e risultati salvati su Supabase

const MAX_UPLOAD_MB = () => window.TT_CONFIG?.maxUploadMb || 500;

const ACCEPTED_MEDIA = 'audio/*,video/*,.mp3,.wav,.m4a,.flac,.ogg,.webm,.aac,.mp4,.mov,.avi,.mkv';

const LANGUAGES = {
  auto: 'Auto',
  it: 'IT',
  en: 'EN',
  es: 'ES',
  fr: 'FR',
  de: 'DE',
  pt: 'PT',
  nl: 'NL',
};

function normalizeTranscription(row) {
  const usage = row.usage || {};
  const progressPercent = progressPercentFromUsage(row.status, usage, row);

  return {
    id: row.id,
    title: row.title || 'Nuova trascrizione',
    lang: LANGUAGES[row.language] || String(row.language || 'auto').toUpperCase(),
    dur: row.duration_label || durationFromSeconds(row.duration_seconds) || `~${row.estimated_minutes || 1} min`,
    date: dateLabel(row.created_at),
    mode: modeLabel(row.mode),
    status: row.status,
    transcriptText: row.transcript_text || '',
    fileName: row.file_name,
    words: row.word_count || countWords(row.transcript_text),
    costCredits: row.cost_credits || 1,
    errorMessage: row.error_message,
    createdAt: row.created_at,
    updatedAt: row.updated_at,
    usage,
    estimatedMinutes: row.estimated_minutes || 1,
    progressPercent,
    progressLabel: progressLabelFromUsage(row.status, usage),
    completedChunks: nonNegativeNumber(usage.completed_chunks),
    totalChunks: nonNegativeNumber(usage.total_chunks),
    currentChunk: nonNegativeNumber(usage.current_chunk),
    processingStep: usage.processing_step || '',
  };
}

function progressPercentFromUsage(status, usage = {}, row = {}) {
  if (status === 'done') return 100;
  if (status === 'error') return 100;

  const explicit = Number(usage.progress_percent);
  if (Number.isFinite(explicit) && explicit > 0) return clampPercent(Math.round(explicit));

  const step = String(usage.processing_step || '').toLowerCase();
  const totalChunks = nonNegativeNumber(usage.total_chunks);
  const completedChunks = Math.min(nonNegativeNumber(usage.completed_chunks), totalChunks || Infinity);

  if (step === 'saving') return 96;
  if (step === 'transcribing') {
    if (!totalChunks) return 25;
    return clampPercent(Math.round(25 + (completedChunks / totalChunks) * 67));
  }
  if (step === 'uploading') return 22;
  if (step === 'prepared') return 18;
  if (step === 'preparing') return 10;
  if (step === 'queued') return 4;

  return estimatedProgressFromTime(row);
}

function progressLabelFromUsage(status, usage = {}) {
  if (status === 'done') return 'Trascrizione pronta';
  if (status === 'error') return 'Qualcosa si è interrotto';

  const step = String(usage.processing_step || '').toLowerCase();

  if (step === 'saving') return 'Prepariamo il risultato...';
  if (step === 'transcribing') return 'Trascrivendo...';
  if (step === 'uploading') return 'Caricamento del file...';
  if (step === 'prepared') return 'Trascrivendo...';
  if (step === 'preparing') return 'Prepariamo il file...';
  return 'Avvio della trascrizione...';
}

function estimatedProgressFromTime(row = {}) {
  const startedAt = new Date(row.created_at || row.createdAt || Date.now()).getTime();
  const estimatedMinutes = Number(row.estimated_minutes || row.estimatedMinutes || 1);
  if (!Number.isFinite(startedAt) || !Number.isFinite(estimatedMinutes) || estimatedMinutes <= 0) return 8;

  const elapsed = Math.max(0, Date.now() - startedAt);
  const total = estimatedMinutes * 60 * 1000;
  return clampPercent(Math.min(85, Math.round(8 + (elapsed / total) * 77)));
}

function nonNegativeNumber(value) {
  const parsed = Math.floor(Number(value));
  return Number.isFinite(parsed) && parsed > 0 ? parsed : 0;
}

function clampPercent(value) {
  return Math.max(0, Math.min(100, value));
}

function durationFromSeconds(seconds) {
  const total = Math.round(Number(seconds || 0));
  if (!total) return '';
  const hours = Math.floor(total / 3600);
  const minutes = Math.floor((total % 3600) / 60);
  const secs = total % 60;
  if (hours) return `${hours}:${String(minutes).padStart(2, '0')}:${String(secs).padStart(2, '0')}`;
  return `${minutes}:${String(secs).padStart(2, '0')}`;
}

function dateLabel(value) {
  if (!value) return 'oggi';
  const date = new Date(value);
  const today = new Date();
  if (date.toDateString() === today.toDateString()) return 'oggi';
  return date.toLocaleDateString('it-IT', { day: 'numeric', month: 'short' });
}

function countWords(text) {
  return String(text || '').trim().split(/\s+/).filter(Boolean).length;
}

function formatNumber(value) {
  return Number(value || 0).toLocaleString('it-IT', {
    maximumFractionDigits: 2,
  });
}

function formatEuroCents(value, currency = 'eur') {
  return new Intl.NumberFormat('it-IT', {
    style: 'currency',
    currency: String(currency || 'eur').toUpperCase(),
  }).format(Number(value || 0) / 100);
}

function creditPacksFromConfig() {
  return window.TT_CONFIG?.billing?.creditPacks || [];
}

function formatFastModeDuration(credits) {
  const totalMinutes = Math.round(Number(credits || 0) / 0.5);
  const hours = Math.floor(totalMinutes / 60);
  const minutes = totalMinutes % 60;
  return `${hours} h ${String(minutes).padStart(2, '0')} min`;
}

function checkoutRedirectState() {
  const params = new URLSearchParams(window.location.search);
  return {
    status: params.get('checkout') || '',
    sessionId: params.get('session_id') || '',
  };
}

function clearCheckoutRedirectState() {
  const url = new URL(window.location.href);
  url.searchParams.delete('checkout');
  url.searchParams.delete('session_id');
  window.history.replaceState({}, '', `${url.pathname}${url.search}${url.hash}`);
}

function formatAdminDate(value) {
  if (!value) return '—';
  return new Date(value).toLocaleString('it-IT', {
    day: '2-digit',
    month: 'short',
    hour: '2-digit',
    minute: '2-digit',
  });
}

function formatAdminBytes(value) {
  const bytes = Number(value || 0);
  if (!bytes) return '0 B';
  const units = ['B', 'KB', 'MB', 'GB'];
  const index = Math.min(units.length - 1, Math.floor(Math.log(bytes) / Math.log(1024)));
  const amount = bytes / Math.pow(1024, index);
  return `${amount.toFixed(index ? 1 : 0)} ${units[index]}`;
}

function estimateCreditsFromFile(file, mode) {
  if (!file) return 0;
  const estMin = Math.max(1, Math.round(file.size / (1024 * 1024)) || 1);
  const multiplier = mode === 'speed' ? 0.5 : mode === 'standard' ? 1 : 3;
  return Math.max(0.5, Number((estMin * multiplier).toFixed(2)));
}

function modeLabel(mode) {
  if (mode === 'speed') return 'Velocità';
  if (mode === 'standard') return 'Standard';
  return 'Qualità';
}

function adminModelStatusLabel(status) {
  if (status === 'compatible') return 'Compatibile';
  if (status === 'available_without_audio_signal') return 'Disponibile';
  return 'Non trovato';
}

function adminModelStatusColor(status) {
  if (status === 'compatible') return 'var(--mint)';
  if (status === 'available_without_audio_signal') return 'var(--sun)';
  return 'var(--coral-soft)';
}

function isSupportedMediaFile(file) {
  if (!file) return false;
  const type = String(file.type || '').toLowerCase();
  const ext = String(file.name || '').toLowerCase().split('.').pop();
  return type.startsWith('audio/') ||
    type.startsWith('video/') ||
    ['aac', 'avi', 'flac', 'm4a', 'm4v', 'mkv', 'mov', 'mp3', 'mp4', 'oga', 'ogg', 'wav', 'webm'].includes(ext);
}

function sleep(ms) {
  return new Promise(resolve => setTimeout(resolve, ms));
}

function useElementWidth() {
  const ref = React.useRef(null);
  const [width, setWidth] = React.useState(0);

  React.useLayoutEffect(() => {
    const element = ref.current;
    if (!element) return undefined;

    const update = () => setWidth(element.getBoundingClientRect().width);
    update();

    if (typeof ResizeObserver === 'function') {
      const observer = new ResizeObserver(entries => {
        setWidth(entries[0]?.contentRect?.width || 0);
      });
      observer.observe(element);
      return () => observer.disconnect();
    }

    window.addEventListener('resize', update);
    return () => window.removeEventListener('resize', update);
  }, []);

  return [ref, width];
}

function safeDownloadName(title, extension) {
  const base = String(title || 'trascrizione')
    .replace(/\.[^.]+$/, '')
    .replace(/[\\/:*?"<>|#]+/g, '-')
    .replace(/\s+/g, ' ')
    .trim()
    .slice(0, 120) || 'trascrizione';
  return `${base}.${extension}`;
}

function downloadText(filename, text, type = 'text/plain;charset=utf-8') {
  const blob = new Blob([String(text || '')], { type });
  const url = URL.createObjectURL(blob);
  const link = document.createElement('a');
  link.href = url;
  link.download = filename;
  link.rel = 'noopener';
  link.style.display = 'none';
  document.body.appendChild(link);
  link.click();
  window.setTimeout(() => {
    link.remove();
    URL.revokeObjectURL(url);
  }, 1000);
}

async function copyTextToClipboard(text) {
  const value = String(text || '');

  if (navigator.clipboard && window.isSecureContext) {
    try {
      await navigator.clipboard.writeText(value);
      return true;
    } catch (_error) {
      // Fall back to the textarea approach below.
    }
  }

  const textarea = document.createElement('textarea');
  textarea.value = value;
  textarea.setAttribute('readonly', '');
  textarea.style.position = 'fixed';
  textarea.style.left = '-9999px';
  textarea.style.top = '0';
  document.body.appendChild(textarea);
  textarea.focus();
  textarea.select();

  try {
    const copied = document.execCommand('copy');
    if (!copied) throw new Error('copy_failed');
    return true;
  } finally {
    textarea.remove();
  }
}

function toSrt(text) {
  const clean = String(text || '').trim();
  return `1\n00:00:00,000 --> 00:59:59,000\n${clean}\n`;
}

function splitTranscript(text) {
  const clean = String(text || '').trim();
  if (!clean) return [];
  const blocks = clean.split(/\n{2,}/).map(p => p.trim()).filter(Boolean);
  if (blocks.length > 1) return blocks;
  return clean.match(/[^.!?]+[.!?]+(\s|$)|[^.!?]+$/g)?.map(p => p.trim()).filter(Boolean) || [clean];
}

// ---------- Sidebar ----------
const UserAvatar = ({ user }) => {
  const [imageFailed, setImageFailed] = React.useState(false);
  const initial = (user.name || user.email || 'T').trim()[0]?.toUpperCase() || 'T';
  const showImage = user.avatarUrl && !imageFailed;

  React.useEffect(() => {
    setImageFailed(false);
  }, [user.avatarUrl]);

  return (
    <div style={{
      width: 36, height: 36, borderRadius:'50%',
      background:'var(--lilac)', border:'2px solid var(--ink)',
      display:'flex', alignItems:'center', justifyContent:'center',
      fontWeight: 700, fontSize: 14, minWidth: 0,
      overflow: 'hidden', flexShrink: 0,
    }}>
      {showImage ? (
        <img
          src={user.avatarUrl}
          alt=""
          referrerPolicy="no-referrer"
          onError={() => setImageFailed(true)}
          style={{ width:'100%', height:'100%', objectFit:'cover', display:'block' }}
        />
      ) : initial}
    </div>
  );
};

const Sidebar = ({ user, history, activeId, onSelect, onNew, onAdmin, onBilling, onSettings, credits, onLogout, loading, adminActive, billingActive, settingsActive }) => (
  <aside data-screen-label="Sidebar" style={{
    width: 320, flexShrink: 0,
    borderRight: '2px solid var(--ink)',
    background: 'var(--cream)',
    display: 'flex', flexDirection: 'column',
    height: '100vh', position: 'sticky', top: 0,
  }}>
    <div style={{ padding: '24px 24px 18px', borderBottom: '2px solid var(--ink)' }}>
      <Logo size={32} />
    </div>

    <div style={{ padding: 20, borderBottom: '2px solid var(--ink)' }}>
      <Btn variant="primary" size="lg" full onClick={onNew}>
        <span style={{ fontSize: 18, lineHeight: 1, marginRight: 4 }}>+</span> Nuova trascrizione
      </Btn>
    </div>

    <div style={{ padding: '18px 20px 8px', display:'flex', justifyContent:'space-between', alignItems:'baseline' }}>
      <span style={{ fontSize: 11, fontWeight: 700, letterSpacing:'0.16em', textTransform:'uppercase' }}>Storico</span>
      <span className="mono" style={{ fontSize: 11, color:'#5a554c' }}>{loading ? 'sync' : `${history.length} file`}</span>
    </div>
    <div style={{ flex: 1, overflowY:'auto', padding: '0 12px 12px' }}>
      {history.length === 0 && !loading && (
        <div style={{ padding:'16px 14px', fontSize: 13, color:'#5a554c' }}>
          Nessuna trascrizione salvata.
        </div>
      )}
      {history.map(h => {
        const active = h.id === activeId;
        return (
          <button key={h.id} onClick={() => onSelect(h.id)}
            style={{
              width: '100%', textAlign:'left',
              padding: '12px 14px', marginBottom: 6,
              background: active ? 'var(--sun)' : 'transparent',
              border: '2px solid', borderColor: active ? 'var(--ink)' : 'transparent',
              boxShadow: active ? 'var(--shadow-sm)' : 'none',
              cursor: 'pointer',
              display:'flex', flexDirection:'column', gap: 4,
              transition:'background 80ms',
            }}
            onMouseEnter={e => { if (!active) e.currentTarget.style.background = 'var(--cream-deep)'; }}
            onMouseLeave={e => { if (!active) e.currentTarget.style.background = 'transparent'; }}
          >
            <div style={{ fontSize: 14, fontWeight: 600, lineHeight: 1.25 }}>{h.title}</div>
            <div style={{ display:'flex', gap: 8, alignItems:'center' }}>
              <span className="mono" style={{
                fontSize: 10, padding:'1px 6px',
                border:'1.5px solid var(--ink)', background:'var(--paper)',
                fontWeight: 600,
              }}>{h.lang}</span>
              <span className="mono" style={{ fontSize: 11, color:'#5a554c' }}>{h.dur}</span>
              <span style={{ fontSize: 11, color:'#5a554c', marginLeft:'auto' }}>{h.date}</span>
            </div>
          </button>
        );
      })}
    </div>

    <div style={{ padding: 20, borderTop: '2px solid var(--ink)', display:'grid', gap: 14, minWidth: 0, overflow:'hidden' }}>
      <CreditMeter credits={credits} max={15} />
      <Btn
        variant={billingActive ? 'ink' : 'sun'}
        size="sm"
        full
        onClick={onBilling}
        style={{ minHeight: 38 }}
      >
        + Ricarica crediti
      </Btn>
      <div style={{ display:'grid', gridTemplateColumns:'36px minmax(0, 1fr) auto auto auto', alignItems:'center', gap: 8, width:'100%', minWidth: 0, overflow:'hidden' }}>
        <UserAvatar user={user} />
        <div style={{ flex: 1, minWidth: 0 }}>
          <div style={{ fontSize: 13, fontWeight: 600, overflow:'hidden', textOverflow:'ellipsis', whiteSpace:'nowrap' }}>{user.name}</div>
          <div style={{ fontSize: 11, color:'#5a554c', overflow:'hidden', textOverflow:'ellipsis', whiteSpace:'nowrap' }}>{user.email}</div>
        </div>
        {user.isAdmin && (
          <button type="button" onClick={onAdmin} title="Admin" aria-label="Admin" style={{
            background: adminActive ? 'var(--sun)' : 'transparent',
            border:'2px solid var(--ink)',
            width: 44, height: 32, display:'flex', alignItems:'center', justifyContent:'center',
            fontSize: 10, fontWeight: 800, letterSpacing:'0.08em',
            padding: 0, flexShrink: 0,
            boxShadow: adminActive ? 'var(--shadow-sm)' : 'none',
          }}>ADM</button>
        )}
        <button type="button" onClick={onSettings} title="Impostazioni" aria-label="Impostazioni" style={{
          background:'transparent', border:'2px solid var(--ink)',
          width: 32, height: 32, display:'flex', alignItems:'center', justifyContent:'center',
          fontSize: 16, fontWeight: 700,
          padding: 0, flexShrink: 0,
          boxShadow: settingsActive ? 'var(--shadow-sm)' : 'none',
        }}>⚙</button>
        <button type="button" onClick={onLogout} title="Esci" style={{
          background:'transparent', border:'2px solid var(--ink)',
          width: 46, height: 32, padding: 0, fontSize: 10, fontWeight: 700, letterSpacing:'0.08em',
          textTransform:'uppercase',
        }}>Esci</button>
      </div>
    </div>
  </aside>
);

const EmptyState = ({ onNew, error }) => (
  <div style={{ flex: 1, display:'flex', alignItems:'center', justifyContent:'center', padding: 40, position:'relative' }}>
    <Sticker kind="polaroid" style={{ top: 80, left: 100, transform:'rotate(-10deg)' }} />
    <Sticker kind="circle"   style={{ bottom: 120, right: 140 }} />
    <Sticker kind="sun"      style={{ top: 140, right: 200, transform:'rotate(15deg)' }} />
    <Sticker kind="star"     style={{ width: 24, height: 24, bottom: 200, left: 220 }} />

    <div style={{ textAlign:'center', maxWidth: 640 }}>
      {error && <Notice tone="error">{error}</Notice>}
      <h1 className="display" style={{ fontSize: 'clamp(56px, 7vw, 96px)', margin: error ? '24px 0 0' : 0 }}>
        SELEZIONA <span className="hl">UN AUDIO</span><br/>
        <span className="outline-text">— OPPURE —</span><br/>
        <span className="hl sun" style={{ transform:'rotate(1deg)' }}>CARICANE UNO NUOVO</span>
      </h1>
      <p style={{ fontSize: 17, marginTop: 28, color:'#3a352e' }}>
        Apri una trascrizione dallo storico per leggerla,<br/>
        oppure trascina un file nuovo per iniziare.
      </p>
      <div style={{ marginTop: 32 }}>
        <Btn variant="primary" size="lg" onClick={onNew}>+ Nuova trascrizione</Btn>
      </div>
    </div>
  </div>
);

const Notice = ({ children, tone = 'error' }) => (
  <div style={{
    display:'inline-block',
    maxWidth: 680,
    padding: '12px 14px',
    background: tone === 'error' ? 'var(--coral-soft)' : 'var(--mint)',
    border:'2px solid var(--ink)',
    boxShadow:'var(--shadow-sm)',
    fontSize: 13,
    textAlign:'left',
  }}>{children}</div>
);

// ---------- New transcription form ----------
const NewTranscription = ({ credits, onCancel, onStart, error }) => {
  const [file, setFile] = React.useState(null);
  const [lang, setLang] = React.useState('it');
  const [mode, setMode] = React.useState('quality');
  const [title, setTitle] = React.useState('');
  const [drag, setDrag] = React.useState(false);
  const [localError, setLocalError] = React.useState('');
  const inputRef = React.useRef(null);
  const [screenRef, screenWidth] = useElementWidth();

  const cost = estimateCreditsFromFile(file, mode);
  const insufficient = cost > credits;
  const compactLayout = screenWidth > 0 && screenWidth < 1040;
  const narrowLayout = screenWidth > 0 && screenWidth < 760;
  const tinyLayout = screenWidth > 0 && screenWidth < 560;
  const screenPadding = tinyLayout ? '24px 18px' : narrowLayout ? '32px 28px' : '40px 56px';
  const headlineSize = tinyLayout ? 40 : narrowLayout ? 48 : compactLayout ? 60 : 76;
  const workspaceColumns = compactLayout ? 'minmax(0, 1fr)' : 'minmax(0, 1fr) 320px';
  const controlsColumns = narrowLayout ? 'minmax(0, 1fr)' : 'minmax(180px, 0.85fr) minmax(0, 1.35fr)';
  const modeColumns = tinyLayout ? 'minmax(0, 1fr)' : 'repeat(3, minmax(0, 1fr))';

  const handleFile = (raw) => {
    if (!raw) return;
    const maxBytes = MAX_UPLOAD_MB() * 1024 * 1024;
    if (!isSupportedMediaFile(raw)) {
      setLocalError('Carica un file audio o video: MP3, WAV, M4A, FLAC, OGG, WEBM, MP4, MOV, AVI o MKV.');
      return;
    }
    if (raw.size > maxBytes) {
      setLocalError(`Il file supera il limite attuale di ${MAX_UPLOAD_MB()} MB.`);
      return;
    }
    const estMin = Math.max(1, Math.round((raw.size / (1024*1024)) || 1));
    setLocalError('');
    setFile({ raw, name: raw.name, size: raw.size, estMin });
    if (!title) setTitle(raw.name.replace(/\.[^.]+$/, ''));
  };

  return (
    <div ref={screenRef} data-screen-label="Nuova trascrizione" style={{ flex: 1, minWidth: 0, padding: screenPadding, position:'relative', overflowY:'auto' }}>
      <div style={{ display:'flex', justifyContent:'space-between', alignItems:'flex-start', gap: 16, flexWrap:'wrap', marginBottom: compactLayout ? 24 : 28 }}>
        <div style={{ minWidth: 0, maxWidth: compactLayout ? 760 : 980 }}>
          <div style={{ fontSize: 12, fontWeight: 700, letterSpacing:'0.18em', textTransform:'uppercase', color:'#5a554c', marginBottom: 8 }}>
            ◇ Nuova
          </div>
          <h1 className="display" style={{ fontSize: headlineSize, margin: 0, lineHeight: 0.95 }}>
            CARICA. <span className="hl">SCEGLI.</span> <span className="outline-text">TRASCRIVI.</span>
          </h1>
        </div>
        <button onClick={onCancel} style={{
          background:'transparent', border:'2px solid var(--ink)',
          padding:'8px 14px', fontSize: 12, fontWeight: 700, letterSpacing:'0.1em', textTransform:'uppercase',
          boxShadow:'var(--shadow-sm)', marginLeft:'auto',
        }}>✕ Annulla</button>
      </div>

      {(error || localError) && <div style={{ marginBottom: 20 }}><Notice>{error || localError}</Notice></div>}

      <div style={{ display:'grid', gridTemplateColumns: workspaceColumns, gap: compactLayout ? 24 : 32, alignItems:'start' }}>
        <div style={{ display:'grid', gap: compactLayout ? 20 : 24, minWidth: 0 }}>
          <div
            onDragOver={e => { e.preventDefault(); setDrag(true); }}
            onDragLeave={() => setDrag(false)}
            onDrop={e => {
              e.preventDefault(); setDrag(false);
              const f = e.dataTransfer.files && e.dataTransfer.files[0];
              if (f) handleFile(f);
            }}
            onClick={() => inputRef.current && inputRef.current.click()}
	            style={{
	              border:'2px dashed var(--ink)',
	              background: drag ? 'var(--sun)' : 'var(--paper)',
	              boxShadow:'var(--shadow-sm)',
	              padding: tinyLayout ? 20 : compactLayout ? 26 : 32,
	              cursor:'pointer',
	              minWidth: 0,
	              overflow:'hidden',
	              transition:'background 100ms',
	            }}>
            <input ref={inputRef} type="file" accept={ACCEPTED_MEDIA} hidden
              onChange={e => handleFile(e.target.files && e.target.files[0])} />
            {!file ? (
	              <div style={{ textAlign:'center', padding: tinyLayout ? '12px 0' : '24px 0', minWidth: 0 }}>
                <div style={{
                  width: 72, height: 72, margin:'0 auto 18px',
                  background:'var(--coral)', border:'2px solid var(--ink)', boxShadow:'var(--shadow-sm)',
                  display:'flex', alignItems:'center', justifyContent:'center',
                }}>
                  <svg width="32" height="32" viewBox="0 0 32 32" fill="none">
                    <path d="M16 4 L16 22 M9 11 L16 4 L23 11" stroke="var(--ink)" strokeWidth="2.5" strokeLinecap="square" fill="none"/>
                    <rect x="6" y="24" width="20" height="3" fill="var(--ink)"/>
                  </svg>
                </div>
	                <div className="display" style={{ fontSize: tinyLayout ? 22 : 28, lineHeight: 1.05, overflowWrap:'anywhere' }}>Trascina qui audio o video</div>
                <div style={{ fontSize: 14, color:'#5a554c', marginTop: 8 }}>
                  oppure <span style={{ textDecoration:'underline', fontWeight: 600 }}>cerca tra i file</span>
                </div>
	                <div className="mono" style={{ fontSize: 11, color:'#5a554c', marginTop: 16, letterSpacing:'0.08em', overflowWrap:'anywhere' }}>
	                  MP3 · WAV · M4A · MP4 · MOV · MKV · max {MAX_UPLOAD_MB()} MB
	                </div>
              </div>
            ) : (
              <div style={{ display:'flex', alignItems:'center', gap: 16 }}>
                <div style={{
                  width: 56, height: 56, flexShrink: 0,
                  background:'var(--mint)', border:'2px solid var(--ink)', boxShadow:'var(--shadow-sm)',
                  display:'flex', alignItems:'center', justifyContent:'center',
                }}>
                  <svg width="24" height="24" viewBox="0 0 24 24">
                    <rect x="3" y="9" width="2" height="6" fill="var(--ink)"/>
                    <rect x="7" y="6" width="2" height="12" fill="var(--ink)"/>
                    <rect x="11" y="3" width="2" height="18" fill="var(--ink)"/>
                    <rect x="15" y="7" width="2" height="10" fill="var(--ink)"/>
                    <rect x="19" y="10" width="2" height="4" fill="var(--ink)"/>
                  </svg>
                </div>
                <div style={{ flex: 1, minWidth: 0 }}>
                  <div style={{ fontSize: 15, fontWeight: 600, overflow:'hidden', textOverflow:'ellipsis', whiteSpace:'nowrap' }}>{file.name}</div>
                  <div className="mono" style={{ fontSize: 12, color:'#5a554c', marginTop: 4 }}>
                    {(file.size / (1024*1024)).toFixed(1)} MB · ~{file.estMin} min stimati
                  </div>
                </div>
                <button onClick={(e) => { e.stopPropagation(); setFile(null); }}
                  style={{ background:'transparent', border:'2px solid var(--ink)', padding:'6px 10px', fontSize: 11, fontWeight: 700, letterSpacing:'0.1em', textTransform:'uppercase' }}>
                  Cambia
                </button>
              </div>
            )}
          </div>

	          <div style={{ display:'grid', gridTemplateColumns: controlsColumns, gap: 20, minWidth: 0 }}>
	            <div style={{ minWidth: 0 }}>
	              <Field label="Lingua dell'audio" hint="Auto-rilevamento se non sei sicuro.">
	                <Select value={lang} onChange={e => setLang(e.target.value)}>
	                  <option value="auto">🌐 Rileva automaticamente</option>
	                  <option value="it">🇮🇹 Italiano</option>
	                  <option value="en">🇬🇧 Inglese</option>
	                  <option value="es">🇪🇸 Spagnolo</option>
	                  <option value="fr">🇫🇷 Francese</option>
	                  <option value="de">🇩🇪 Tedesco</option>
	                  <option value="pt">🇵🇹 Portoghese</option>
	                  <option value="nl">🇳🇱 Olandese</option>
	                </Select>
	              </Field>
	            </div>

	            <div style={{ minWidth: 0 }}>
	              <Field label="Modalità">
	                <div style={{ display:'grid', gridTemplateColumns: modeColumns, gap: 0, border:'2px solid var(--ink)', boxShadow:'var(--shadow-sm)', minWidth: 0, overflow:'hidden' }}>
	                {[
	                  { id:'speed',   label:'Velocità', sub:'Per bozze rapide', cost: '0.5×' },
                  { id:'standard', label:'Standard', sub:'Equilibrata', cost: '1×' },
                  { id:'quality', label:'Qualità',  sub:'Super accurata', cost: '3×' },
                ].map((opt, i) => {
                  const sel = mode === opt.id;
                  return (
	                    <button key={opt.id} type="button" onClick={() => setMode(opt.id)}
	                      style={{
	                        background: sel ? 'var(--ink)' : 'var(--paper)',
	                        color: sel ? '#FFFCEC' : 'var(--ink)',
	                        border:'none',
	                        borderRight: !tinyLayout && i < 2 ? '2px solid var(--ink)' : 'none',
	                        borderBottom: tinyLayout && i < 2 ? '2px solid var(--ink)' : 'none',
	                        padding:'14px 12px', textAlign:'left', cursor:'pointer',
	                        minWidth: 0,
	                      }}>
	                      <div style={{ display:'flex', justifyContent:'space-between', alignItems:'center', gap: 6, flexWrap:'wrap', marginBottom: 4, minWidth: 0 }}>
	                        <span style={{ fontSize: tinyLayout ? 13 : 14, fontWeight: 700, letterSpacing:'0.04em', textTransform:'uppercase', minWidth: 0 }}>{opt.label}</span>
	                        <span className="mono" style={{
	                          fontSize: 10, padding:'1px 6px',
                          background: sel ? 'var(--coral)' : 'var(--cream-deep)',
                          color:'var(--ink)',
                          border:'1.5px solid', borderColor: sel ? 'var(--coral)' : 'var(--ink)',
                          fontWeight: 600,
                        }}>{opt.cost}</span>
                      </div>
                      <div style={{ fontSize: 11, opacity: sel ? 0.8 : 0.7 }}>{opt.sub}</div>
                    </button>
                  );
                })}
	                </div>
	              </Field>
	            </div>
	          </div>

	          <div style={{ minWidth: 0 }}>
	            <Field label="Titolo" hint="Se lasci vuoto useremo il nome del file.">
	              <TextInput value={title} onChange={e => setTitle(e.target.value)} placeholder="es. Riunione team prodotto — Q2" />
	            </Field>
	          </div>
	        </div>

	        <div style={{
	          position: compactLayout ? 'static' : 'sticky',
	          top: compactLayout ? 'auto' : 24,
	          background:'var(--paper)', border:'2px solid var(--ink)', boxShadow:'var(--shadow-lg)',
	          padding: 24,
	          minWidth: 0,
	        }}>
          <div style={{ fontSize: 11, fontWeight: 700, letterSpacing:'0.16em', textTransform:'uppercase', color:'#5a554c', marginBottom: 14 }}>Riepilogo</div>

          <div style={{ display:'grid', gap: 12, fontSize: 14 }}>
            <Row k="File"     v={file ? file.name : <span style={{ color:'#5a554c' }}>—</span>} />
            <Row k="Durata"   v={file ? <span className="mono">~{file.estMin} min</span> : <span style={{ color:'#5a554c' }}>—</span>} />
            <Row k="Lingua"   v={lang === 'auto' ? 'Auto' : lang.toUpperCase()} />
            <Row k="Modalità" v={modeLabel(mode)} />
          </div>

          <hr style={{ border:'none', borderTop:'2px solid var(--ink)', margin:'18px 0' }} />

          <div style={{ display:'flex', justifyContent:'space-between', alignItems:'baseline', marginBottom: 6 }}>
            <span style={{ fontSize: 11, fontWeight: 700, letterSpacing:'0.16em', textTransform:'uppercase' }}>Costo</span>
            <span className="display" style={{ fontSize: 32 }}>
              {cost > 0 ? formatNumber(cost) : '—'}
              <span style={{ fontSize: 13, marginLeft: 6, fontFamily:'Space Grotesk', fontWeight: 700 }}>crediti</span>
            </span>
          </div>
          <div style={{ fontSize: 12, color:'#5a554c' }}>
            Disponibili: <span className="mono">{formatNumber(credits)}</span> · Dopo: <span className="mono">{formatNumber(Math.max(0, credits - cost))}</span>
          </div>

          {insufficient && (
            <div style={{
              marginTop: 14, padding: 10,
              background:'var(--coral-soft)', border:'2px solid var(--ink)',
              fontSize: 12,
            }}>Crediti insufficienti per questo file.</div>
          )}

          <div style={{ marginTop: 22 }}>
            <Btn variant="primary" size="lg" full
              disabled={!file || insufficient}
              onClick={() => onStart({ file, lang, mode, title, cost })}>
              Trascrivi →
            </Btn>
          </div>
          <div style={{ fontSize: 11, color:'#5a554c', textAlign:'center', marginTop: 10 }}>
            Usi i crediti solo quando avvii la trascrizione.
          </div>
        </div>
      </div>
    </div>
  );
};

const Row = ({ k, v }) => (
  <div style={{ display:'flex', justifyContent:'space-between', gap: 12 }}>
    <span style={{ color:'#5a554c' }}>{k}</span>
    <span style={{ fontWeight: 600, textAlign:'right', maxWidth:'65%', overflow:'hidden', textOverflow:'ellipsis', whiteSpace:'nowrap' }}>{v}</span>
  </div>
);

// ---------- Processing ----------
const Processing = ({ job }) => {
  const [simulatedPct, setSimulatedPct] = React.useState(8);
  const steps = [
    'Prepariamo il file...',
    'Caricamento del file...',
    'Trascrivendo...',
    'Rifinendo il testo...',
    'Quasi pronto...',
  ];

  React.useEffect(() => {
    setSimulatedPct(8);
    const id = setInterval(() => {
      setSimulatedPct(p => Math.min(84, p + (p < 50 ? 5 : 1)));
    }, 900);
    return () => clearInterval(id);
  }, [job?.id]);

  const realPct = Number(job?.progressPercent);
  const hasRealProgress = Number.isFinite(realPct) && realPct > 0;
  const pct = hasRealProgress ? clampPercent(Math.round(realPct)) : simulatedPct;
  const step = hasRealProgress
    ? job.progressLabel
    : steps[Math.min(steps.length - 1, Math.floor((pct / 100) * steps.length))];
  const fileName = job?.fileName || job?.file?.name || 'File in elaborazione';

  return (
    <div data-screen-label="Trascrizione in corso" style={{ flex:1, display:'flex', alignItems:'center', justifyContent:'center', padding: 40, position:'relative' }}>
      <Sticker kind="circle" style={{ top: 80, right: 120 }} />
      <Sticker kind="sun"    style={{ bottom: 100, left: 140, transform:'rotate(-10deg)' }} />
      <Sticker kind="star"   style={{ width: 24, height: 24, top: 160, left: 200 }} />

      <div style={{ width: '100%', maxWidth: 640, textAlign:'center' }}>
        <div className="mono" style={{ fontSize: 12, fontWeight: 600, letterSpacing:'0.18em', textTransform:'uppercase', color:'#5a554c', marginBottom: 16 }}>
          ◇◇◇ in lavorazione
        </div>
        <h1 className="display" style={{ fontSize: 'clamp(48px, 6vw, 80px)', margin: 0 }}>
          STIAMO <span className="hl">ASCOLTANDO</span><br/>
          <span className="outline-text">PER TE.</span>
        </h1>

        <div style={{ display:'flex', alignItems:'flex-end', justifyContent:'center', gap: 5, marginTop: 36, height: 80 }}>
          {Array.from({length: 28}).map((_, i) => (
            <div key={i} style={{
              width: 8, background: i % 3 === 0 ? 'var(--coral)' : 'var(--ink)',
              border:'2px solid var(--ink)', boxShadow:'2px 2px 0 0 var(--ink)',
              animation: `bar ${0.6 + (i % 5) * 0.15}s ease-in-out ${i * 0.05}s infinite alternate`,
              height: 16,
            }} />
          ))}
        </div>
        <style>{`@keyframes bar { from { height: 16px } to { height: 50px } }`}</style>

        <div style={{ marginTop: 40, padding: 20, background:'var(--paper)', border:'2px solid var(--ink)', boxShadow:'var(--shadow-sm)', textAlign:'left' }}>
          <div style={{ display:'flex', justifyContent:'space-between', marginBottom: 10, fontSize: 13 }}>
            <span style={{ fontWeight: 600 }}>{step}</span>
            <span className="mono" style={{ fontWeight: 600 }}>{pct}%</span>
          </div>
          <div style={{ height: 14, border: '2px solid var(--ink)', background: 'var(--cream-deep)' }}>
            <div style={{ height: '100%', width: `${pct}%`, background: 'var(--coral)', transition: 'width 250ms' }} />
          </div>
          <div style={{ marginTop: 12, fontSize: 12, color:'#5a554c' }}>
            {fileName} · modalità {modeLabel(job?.mode)}
          </div>
        </div>
      </div>
    </div>
  );
};

// ---------- Result view ----------
const Result = ({ item }) => {
  const [copied, setCopied] = React.useState(false);
  const [actionMessage, setActionMessage] = React.useState('');
  const [actionTone, setActionTone] = React.useState('info');
  const actionTimerRef = React.useRef(null);
  const paragraphs = splitTranscript(item.transcriptText);
  const fullText = item.transcriptText || '';

  React.useEffect(() => {
    return () => window.clearTimeout(actionTimerRef.current);
  }, []);

  const showAction = (message, tone = 'info') => {
    setActionTone(tone);
    setActionMessage(message);
    window.clearTimeout(actionTimerRef.current);
    actionTimerRef.current = window.setTimeout(() => setActionMessage(''), 2600);
  };

  const copy = async () => {
    try {
      await copyTextToClipboard(fullText);
      setCopied(true);
      setTimeout(() => setCopied(false), 1500);
      showAction('Testo copiato negli appunti.');
    } catch (_error) {
      showAction('Non sono riuscito a copiare automaticamente.', 'error');
    }
  };

  const downloadTranscript = (extension, content, type) => {
    try {
      downloadText(safeDownloadName(item.title, extension), content, type);
      showAction(`Download .${extension} avviato.`);
    } catch (_error) {
      showAction(`Non sono riuscito a creare il file .${extension}.`, 'error');
    }
  };

  return (
    <div data-screen-label="Trascrizione pronta" style={{ flex:1, padding:'40px 56px', overflowY:'auto', position:'relative' }}>
      <div style={{ display:'flex', justifyContent:'space-between', alignItems:'flex-start', gap: 24, flexWrap:'wrap' }}>
        <div style={{ minWidth: 0, flex:'1 1 420px' }}>
          <div style={{ display:'inline-block', marginBottom: 14 }}>
            <span className="hl mint" style={{ fontSize: 11, fontWeight: 700, letterSpacing:'0.16em', textTransform:'uppercase', padding:'4px 10px' }}>
              ✓ Pronta
            </span>
          </div>
          <h1 className="display" style={{ fontSize: 'clamp(40px, 4.5vw, 64px)', margin: 0, lineHeight: 0.98 }}>
            {item.title}
          </h1>
          <div style={{ display:'flex', gap: 14, marginTop: 18, flexWrap:'wrap', fontSize: 13 }}>
            <Meta k="Durata"  v={item.dur} />
            <Meta k="Lingua"  v={item.lang} />
            <Meta k="Modalità" v={item.mode} />
            <Meta k="Parole"   v={item.words || '—'} />
            <Meta k="Crediti"  v={item.costCredits} />
            <Meta k="Data"     v={item.date} />
          </div>
        </div>

        <div style={{ display:'flex', gap: 10, flexWrap:'wrap' }}>
          <Btn variant="ghost" size="md" onClick={copy}>{copied ? '✓ Copiato' : '⎘ Copia'}</Btn>
          <Btn variant="sun" size="md" onClick={() => downloadTranscript('txt', fullText)}>↓ .txt</Btn>
          <Btn variant="sun" size="md" onClick={() => downloadTranscript('srt', toSrt(fullText), 'application/x-subrip;charset=utf-8')}>↓ .srt</Btn>
        </div>
      </div>

      {actionMessage && (
        <div style={{
          marginTop: 18,
          display:'inline-block',
          background: actionTone === 'error' ? 'var(--coral-soft)' : 'var(--mint)',
          border:'2px solid var(--ink)',
          boxShadow:'var(--shadow-sm)',
          padding:'10px 12px',
          fontSize: 13,
          fontWeight: 600,
        }}>
          {actionMessage}
        </div>
      )}

      <div style={{ marginTop: 28, background:'var(--paper)', border:'2px solid var(--ink)', boxShadow:'var(--shadow-lg)', padding: '32px 36px' }}>
        <div style={{ display:'flex', justifyContent:'space-between', borderBottom:'2px solid var(--ink)', paddingBottom: 12, marginBottom: 24 }}>
          <span style={{ fontSize: 11, fontWeight: 700, letterSpacing:'0.16em', textTransform:'uppercase' }}>Trascrizione</span>
          <span className="mono" style={{ fontSize: 11, color:'#5a554c' }}>testo completo</span>
        </div>
        <div style={{ display:'grid', gap: 18 }}>
          {paragraphs.length ? paragraphs.map((text, i) => (
            <div key={i} style={{ display:'grid', gridTemplateColumns:'56px 1fr', gap: 16, alignItems:'baseline' }}>
              <span className="mono" style={{ fontSize: 12, color:'#5a554c' }}>{String(i + 1).padStart(2, '0')}</span>
              <span style={{ fontSize: 16, lineHeight: 1.7, textWrap:'pretty' }}>{text}</span>
            </div>
          )) : (
            <div style={{ color:'#5a554c', fontSize: 14 }}>Nessun testo disponibile.</div>
          )}
        </div>
      </div>
    </div>
  );
};

const Meta = ({ k, v }) => (
  <div style={{ display:'flex', alignItems:'center', gap: 6 }}>
    <span style={{ fontSize: 11, fontWeight: 700, letterSpacing:'0.14em', textTransform:'uppercase', color:'#5a554c' }}>{k}</span>
    <span className="mono" style={{
      fontSize: 12, fontWeight: 600,
      padding:'2px 8px', border:'1.5px solid var(--ink)', background:'var(--paper)',
    }}>{v}</span>
  </div>
);

// ---------- Billing ----------
const BillingPanel = ({ api, credits, message, tone, onMessage }) => {
  const [loadingPack, setLoadingPack] = React.useState('');
  const [screenRef, screenWidth] = useElementWidth();
  const packs = creditPacksFromConfig();
  const compact = screenWidth > 0 && screenWidth < 980;
  const tiny = screenWidth > 0 && screenWidth < 620;
  const columns = compact ? 'minmax(0, 1fr)' : 'repeat(3, minmax(0, 1fr))';

  const startCheckout = async (pack) => {
    if (loadingPack) return;

    setLoadingPack(pack.id);
    onMessage('', 'info');
    try {
      const result = await api.createCreditCheckout(pack.id);
      if (!result.url) throw new Error('Pagina di pagamento non disponibile.');
      window.location.assign(result.url);
    } catch (error) {
      onMessage(error.message || 'Non siamo riusciti ad aprire la pagina di pagamento.', 'error');
      setLoadingPack('');
    }
  };

  return (
    <div ref={screenRef} data-screen-label="Ricarica crediti" style={{ flex: 1, padding: tiny ? '32px 24px' : '40px 56px', overflowY:'auto', position:'relative' }}>
      <Sticker kind="sun" style={{ top: 64, right: 120, transform:'rotate(6deg)' }} />
      <Sticker kind="mint" style={{ bottom: 110, left: 90 }} />

      <div style={{ display:'flex', justifyContent:'space-between', alignItems:'flex-start', gap: 24, flexWrap:'wrap' }}>
        <div style={{ minWidth: 0 }}>
          <div style={{ fontSize: 12, fontWeight: 700, letterSpacing:'0.18em', textTransform:'uppercase', color:'#5a554c', marginBottom: 8 }}>
            ◇ Crediti
          </div>
          <h1 className="display" style={{ fontSize: 'clamp(48px, 5.5vw, 82px)', margin: 0, lineHeight: 0.95 }}>
            RICARICA. <span className="hl">SCEGLI.</span><br/>
            <span className="outline-text">TRASCRIVI.</span>
          </h1>
        </div>

        <div style={{
          background:'var(--paper)',
          border:'2px solid var(--ink)',
          boxShadow:'var(--shadow-sm)',
          padding:'14px 16px',
          minWidth: 180,
        }}>
          <div style={{ fontSize: 11, fontWeight: 700, letterSpacing:'0.16em', textTransform:'uppercase', color:'#5a554c' }}>Saldo</div>
          <div className="display" style={{ fontSize: 34, lineHeight: 1, marginTop: 6 }}>{formatNumber(credits)}</div>
        </div>
      </div>

      {message && (
        <div style={{ marginTop: 24 }}>
          <Notice tone={tone === 'error' ? 'error' : 'info'}>{message}</Notice>
        </div>
      )}

      {packs.length === 0 ? (
        <div style={{ marginTop: 32 }}>
          <Notice tone="error">Le ricariche non sono disponibili in questo momento.</Notice>
        </div>
      ) : (
        <div style={{ display:'grid', gridTemplateColumns: columns, gap: 18, marginTop: 34, alignItems:'stretch' }}>
          {packs.map(pack => {
            const featured = pack.featured === true;
            const busy = loadingPack === pack.id;
            return (
              <section key={pack.id} style={{
                position:'relative',
                background: featured ? 'var(--ink)' : 'var(--paper)',
                color: featured ? '#FFFCEC' : 'var(--ink)',
                border:'2px solid var(--ink)',
                boxShadow: featured ? '8px 8px 0 0 var(--coral)' : 'var(--shadow-lg)',
                padding: tiny ? 22 : 26,
                minHeight: compact ? 0 : 340,
                display:'flex',
                flexDirection:'column',
                justifyContent:'space-between',
              }}>
                {featured && (
                  <div style={{
                    position:'absolute',
                    top: -14,
                    left: 20,
                    background:'var(--coral)',
                    color:'var(--ink)',
                    border:'2px solid var(--ink)',
                    padding:'5px 10px',
                    fontSize: 10,
                    fontWeight: 900,
                    letterSpacing:'0.1em',
                    textTransform:'uppercase',
                    boxShadow:'var(--shadow-sm)',
                  }}>Più richiesto</div>
                )}

                <div>
                  <div style={{ display:'grid', gridTemplateColumns:'minmax(0, 1fr)', gap: 8, marginBottom: 18 }}>
                    <h2 className="display" style={{
                      fontSize: pack.name.length > 11 ? 29 : 34,
                      lineHeight: 1,
                      margin: 0,
                      overflowWrap:'anywhere',
                    }}>{pack.name}</h2>
                    <span className="mono" style={{
                      justifySelf:'start',
                      fontSize: 11,
                      padding:'3px 7px',
                      border:'1.5px solid',
                      borderColor: featured ? '#FFFCEC' : 'var(--ink)',
                      color: featured ? '#FFFCEC' : 'var(--ink)',
                    }}>una tantum</span>
                  </div>

                  <div className="display" style={{ fontSize: tiny ? 44 : 52, lineHeight: 0.95 }}>
                    +{formatNumber(pack.credits)}
                  </div>
                  <div style={{ fontSize: 13, fontWeight: 700, letterSpacing:'0.12em', textTransform:'uppercase', marginTop: 8 }}>
                    crediti
                  </div>

                  <div style={{ marginTop: 24, paddingTop: 18, borderTop:'2px solid', borderColor: featured ? '#FFFCEC' : 'var(--ink)' }}>
                    <div className="display" style={{ fontSize: 42 }}>
                      {formatEuroCents(pack.unitAmount, pack.currency)}
                    </div>
                    <div className="mono" style={{ fontSize: 11, opacity: 0.78, marginTop: 4 }}>
                      Fino a {formatFastModeDuration(pack.credits)} in modalità veloce
                    </div>
                  </div>
                </div>

                <Btn
                  variant={featured ? 'primary' : 'ink'}
                  size="lg"
                  full
                  disabled={Boolean(loadingPack)}
                  onClick={() => startCheckout(pack)}
                  style={{ marginTop: 26 }}
                >
                  {busy ? 'Apertura...' : 'Acquista →'}
                </Btn>
              </section>
            );
          })}
        </div>
      )}
    </div>
  );
};

// ---------- Settings ----------
const Settings = ({ user, supabaseClient, recoveryMode, onRecoveryHandled }) => {
  const [email, setEmail] = React.useState(user.email || '');
  const [password, setPassword] = React.useState('');
  const [confirm, setConfirm] = React.useState('');
  const [loading, setLoading] = React.useState('');
  const [message, setMessage] = React.useState('');
  const [tone, setTone] = React.useState('info');

  const redirectTo = window.TT_CONFIG?.passwordResetRedirectUrl || `${window.location.origin}/app?recovery=1`;

  const sendRecoveryEmail = async (event) => {
    event.preventDefault();
    if (!email || loading) return;

    setLoading('email');
    setMessage('');
    try {
      const { error } = await supabaseClient.auth.resetPasswordForEmail(email, { redirectTo });
      if (error) throw error;
      setTone('info');
      setMessage('Link di recupero inviato. Controlla la tua email.');
    } catch (error) {
      setTone('error');
      setMessage(error.message || 'Non siamo riusciti a inviare il link.');
    } finally {
      setLoading('');
    }
  };

  const updatePassword = async (event) => {
    event.preventDefault();
    if (loading) return;

    if (password.length < 6) {
      setTone('error');
      setMessage('La nuova password deve avere almeno 6 caratteri.');
      return;
    }
    if (password !== confirm) {
      setTone('error');
      setMessage('Le password non coincidono.');
      return;
    }

    setLoading('password');
    setMessage('');
    try {
      const { error } = await supabaseClient.auth.updateUser({ password });
      if (error) throw error;
      setPassword('');
      setConfirm('');
      setTone('info');
      setMessage('Password aggiornata.');
      onRecoveryHandled();
      if (window.location.search.includes('recovery=1')) {
        window.history.replaceState({}, '', '/app');
      }
    } catch (error) {
      setTone('error');
      setMessage(error.message || 'Non siamo riusciti ad aggiornare la password.');
    } finally {
      setLoading('');
    }
  };

  return (
    <div data-screen-label="Impostazioni" style={{ flex: 1, padding:'40px 56px', overflowY:'auto', position:'relative' }}>
      <Sticker kind="mint" style={{ top: 64, right: 120 }} />
      <Sticker kind="star" style={{ width: 22, height: 22, bottom: 120, left: 120 }} />

      <div style={{ maxWidth: 900 }}>
        <div style={{ fontSize: 12, fontWeight: 700, letterSpacing:'0.18em', textTransform:'uppercase', color:'#5a554c', marginBottom: 8 }}>
          ⚙ Account
        </div>
        <h1 className="display" style={{ fontSize: 'clamp(48px, 5.2vw, 76px)', margin: 0 }}>
          IMPOSTA. <span className="hl">RECUPERA.</span><br/>
          <span className="outline-text">RIPARTI.</span>
        </h1>

        {message && (
          <div style={{ marginTop: 24 }}>
            <Notice tone={tone === 'error' ? 'error' : 'info'}>{message}</Notice>
          </div>
        )}

        <div style={{ display:'grid', gridTemplateColumns:'minmax(0,1fr) minmax(0,1fr)', gap: 28, marginTop: 32 }}>
          <form onSubmit={sendRecoveryEmail} style={{
            background:'var(--paper)', border:'2px solid var(--ink)', boxShadow:'var(--shadow-lg)',
            padding: 28,
          }}>
            <div style={{ display:'flex', justifyContent:'space-between', gap: 16, alignItems:'baseline', marginBottom: 22 }}>
              <span style={{ fontSize: 11, fontWeight: 700, letterSpacing:'0.16em', textTransform:'uppercase' }}>Recupero password</span>
              <span className="mono" style={{ fontSize: 11, color:'#5a554c' }}>email</span>
            </div>
            <Field label="Email account">
              <TextInput type="email" required value={email} onChange={e => setEmail(e.target.value)} placeholder="tu@dominio.it" />
            </Field>
            <div style={{ marginTop: 22 }}>
              <Btn type="submit" variant="primary" size="lg" full disabled={loading === 'email'}>
                {loading === 'email' ? 'Invio...' : 'Invia link →'}
              </Btn>
            </div>
          </form>

          <form onSubmit={updatePassword} style={{
            background: recoveryMode ? 'var(--paper)' : 'var(--cream-deep)',
            border:'2px solid var(--ink)', boxShadow:'var(--shadow-lg)',
            padding: 28,
            opacity: recoveryMode ? 1 : 0.72,
          }}>
            <div style={{ display:'flex', justifyContent:'space-between', gap: 16, alignItems:'baseline', marginBottom: 22 }}>
              <span style={{ fontSize: 11, fontWeight: 700, letterSpacing:'0.16em', textTransform:'uppercase' }}>Nuova password</span>
              <span className="mono" style={{ fontSize: 11, color:'#5a554c' }}>{recoveryMode ? 'sbloccata' : 'link email'}</span>
            </div>
            <div style={{ display:'grid', gap: 18 }}>
              <Field label="Password">
                <TextInput type="password" minLength={6} disabled={!recoveryMode} value={password} onChange={e => setPassword(e.target.value)} placeholder={'•'.repeat(8)} />
              </Field>
              <Field label="Conferma">
                <TextInput type="password" minLength={6} disabled={!recoveryMode} value={confirm} onChange={e => setConfirm(e.target.value)} placeholder={'•'.repeat(8)} />
              </Field>
            </div>
            <div style={{ marginTop: 22 }}>
              <Btn type="submit" variant="ink" size="lg" full disabled={!recoveryMode || loading === 'password'}>
                {loading === 'password' ? 'Salvataggio...' : 'Aggiorna password →'}
              </Btn>
            </div>
          </form>
        </div>
      </div>
    </div>
  );
};

// ---------- Admin ----------
const AdminPanel = ({ api, currentUser, onCreditsChanged }) => {
  const [dashboard, setDashboard] = React.useState(null);
  const [query, setQuery] = React.useState('');
  const [drafts, setDrafts] = React.useState({});
  const [modelSettings, setModelSettings] = React.useState(null);
  const [modelDrafts, setModelDrafts] = React.useState({});
  const [modelDefaults, setModelDefaults] = React.useState({});
  const [modelCheck, setModelCheck] = React.useState(null);
  const [loading, setLoading] = React.useState(true);
  const [saving, setSaving] = React.useState('');
  const [modelSaving, setModelSaving] = React.useState(false);
  const [modelChecking, setModelChecking] = React.useState(false);
  const [message, setMessage] = React.useState('');
  const [tone, setTone] = React.useState('info');

  const loadAdmin = React.useCallback(async () => {
    setLoading(true);
    setMessage('');
    try {
      const [data, modelData] = await Promise.all([
        api.getAdminDashboard(),
        api.getAdminModels(),
      ]);
      setDashboard(data);
      const nextDrafts = {};
      for (const user of data.users || []) {
        nextDrafts[user.id] = String(user.creditsRemaining);
      }
      setDrafts(nextDrafts);
      setModelSettings(modelData.models || {});
      setModelDrafts(modelData.models || {});
      setModelDefaults(modelData.defaults || {});
    } catch (error) {
      setTone('error');
      setMessage(error.message || 'Dashboard admin non disponibile.');
    } finally {
      setLoading(false);
    }
  }, [api]);

  React.useEffect(() => {
    loadAdmin();
  }, [loadAdmin]);

  const users = dashboard?.users || [];
  const needle = query.trim().toLowerCase();
  const filteredUsers = needle
    ? users.filter(user => {
        return String(user.email || '').toLowerCase().includes(needle) ||
          String(user.name || '').toLowerCase().includes(needle);
      })
    : users;

  const saveCredits = async (user) => {
    const credits = Number(drafts[user.id]);
    setSaving(user.id);
    setMessage('');
    try {
      await api.updateAdminCredits(user.id, credits);
      await loadAdmin();
      if (user.id === currentUser.id) onCreditsChanged(credits);
      setTone('info');
      setMessage(`Crediti aggiornati per ${user.email}.`);
    } catch (error) {
      setTone('error');
      setMessage(error.message || 'Non siamo riusciti ad aggiornare i crediti.');
    } finally {
      setSaving('');
    }
  };

  const saveModels = async () => {
    setModelSaving(true);
    setMessage('');
    try {
      const result = await api.updateAdminModels(modelDrafts);
      setModelSettings(result.models);
      setModelDrafts(result.models);
      window.TT_CONFIG = {
        ...(window.TT_CONFIG || {}),
        models: result.models,
      };
      setTone('info');
      setMessage('Modelli AI aggiornati.');
    } catch (error) {
      setTone('error');
      setMessage(error.message || 'Non siamo riusciti ad aggiornare i modelli.');
    } finally {
      setModelSaving(false);
    }
  };

  const verifyModels = async () => {
    setModelChecking(true);
    setMessage('');
    try {
      const result = await api.verifyAdminModels(modelDrafts);
      setModelCheck(result);
      setTone('info');
      setMessage('Verifica modelli completata.');
    } catch (error) {
      setTone('error');
      setMessage(error.message || 'Verifica del motore AI non riuscita.');
    } finally {
      setModelChecking(false);
    }
  };

  const summary = dashboard?.summary || {};
  const modelModes = ['speed', 'standard', 'quality'];
  const modelsChanged = JSON.stringify(modelDrafts) !== JSON.stringify(modelSettings || {});

  return (
    <div data-screen-label="Admin" style={{ flex: 1, padding:'40px 56px', overflowY:'auto', position:'relative' }}>
      <div style={{ display:'flex', justifyContent:'space-between', alignItems:'flex-start', gap: 24, flexWrap:'wrap' }}>
        <div>
          <div style={{ fontSize: 12, fontWeight: 700, letterSpacing:'0.18em', textTransform:'uppercase', color:'#5a554c', marginBottom: 8 }}>
            ◆ Admin
          </div>
          <h1 className="display" style={{ fontSize: 'clamp(48px, 5.2vw, 76px)', margin: 0 }}>
            UTENTI. <span className="hl">CREDITI.</span><br/>
            <span className="outline-text">UTILIZZI.</span>
          </h1>
        </div>

        <Btn variant="ghost" size="md" onClick={loadAdmin} disabled={loading}>
          {loading ? 'Sync...' : 'Aggiorna'}
        </Btn>
      </div>

      {message && (
        <div style={{ marginTop: 24 }}>
          <Notice tone={tone === 'error' ? 'error' : 'info'}>{message}</Notice>
        </div>
      )}

      <div style={{ display:'grid', gridTemplateColumns:'repeat(4, minmax(150px, 1fr))', gap: 16, marginTop: 32 }}>
        <AdminStat label="Utenti" value={summary.totalUsers} accent="var(--coral)" />
        <AdminStat label="Trascrizioni" value={summary.totalTranscriptions} accent="var(--sun)" />
        <AdminStat label="Crediti usati" value={summary.creditsUsed} accent="var(--mint)" />
        <AdminStat label="File elaborati" value={formatAdminBytes(summary.storedBytes)} accent="var(--lilac)" />
      </div>

      <div style={{ display:'grid', gridTemplateColumns:'minmax(0, 1fr) 340px', gap: 24, marginTop: 28, alignItems:'start' }}>
        <section style={{ minWidth: 0 }}>
          <div style={{ display:'flex', justifyContent:'space-between', alignItems:'center', gap: 16, marginBottom: 14 }}>
            <div>
              <div style={{ fontSize: 11, fontWeight: 700, letterSpacing:'0.16em', textTransform:'uppercase' }}>Utenti registrati</div>
              <div style={{ fontSize: 12, color:'#5a554c', marginTop: 4 }}>
                {formatNumber(filteredUsers.length)} visibili · {formatNumber(summary.totalAdmins)} admin
              </div>
            </div>
            <TextInput
              value={query}
              onChange={e => setQuery(e.target.value)}
              placeholder="Cerca nome o email"
              style={{ width: 260, background:'var(--cream)' }}
            />
          </div>

          <div style={{
            background:'var(--paper)', border:'2px solid var(--ink)', boxShadow:'var(--shadow-lg)',
            overflowX:'auto',
          }}>
            <div style={{
              minWidth: 980,
              display:'grid',
              gridTemplateColumns:'minmax(240px, 1.4fr) 110px 120px 120px 160px 150px',
              gap: 0,
              borderBottom:'2px solid var(--ink)',
              background:'var(--cream-deep)',
              fontSize: 11,
              fontWeight: 700,
              letterSpacing:'0.14em',
              textTransform:'uppercase',
            }}>
              {['Account', 'Ruolo', 'File', 'Crediti usati', 'Ultimo uso', 'Saldo crediti'].map(label => (
                <div key={label} style={{ padding:'12px 14px', borderRight:'2px solid var(--ink)' }}>{label}</div>
              ))}
            </div>

            {loading && (
              <div style={{ padding: 22, fontSize: 13, color:'#5a554c' }}>Caricamento dashboard admin...</div>
            )}

            {!loading && filteredUsers.map(user => (
              <div key={user.id} style={{
                minWidth: 980,
                display:'grid',
                gridTemplateColumns:'minmax(240px, 1.4fr) 110px 120px 120px 160px 150px',
                borderBottom:'2px solid var(--ink)',
                alignItems:'center',
              }}>
                <div style={{ padding:'14px', minWidth: 0 }}>
                  <div style={{ fontSize: 14, fontWeight: 700, overflow:'hidden', textOverflow:'ellipsis', whiteSpace:'nowrap' }}>{user.name || 'Utente'}</div>
                  <div style={{ fontSize: 12, color:'#5a554c', overflow:'hidden', textOverflow:'ellipsis', whiteSpace:'nowrap' }}>{user.email}</div>
                  <div className="mono" style={{ fontSize: 10, color:'#5a554c', marginTop: 5 }}>
                    dal {formatAdminDate(user.createdAt)}
                  </div>
                </div>
                <div style={{ padding:'14px' }}>
                  <span style={{
                    display:'inline-flex',
                    padding:'4px 8px',
                    border:'1.5px solid var(--ink)',
                    background: user.isAdmin ? 'var(--sun)' : 'var(--cream)',
                    fontSize: 10,
                    fontWeight: 800,
                    letterSpacing:'0.08em',
                    textTransform:'uppercase',
                  }}>{user.isAdmin ? 'Admin' : 'User'}</span>
                </div>
                <div className="mono" style={{ padding:'14px', fontSize: 13 }}>
                  {formatNumber(user.usage.transcriptions)}
                  <div style={{ fontSize: 10, color:'#5a554c', marginTop: 4 }}>
                    {formatNumber(user.usage.completed)} ok · {formatNumber(user.usage.failed)} err
                  </div>
                </div>
                <div className="mono" style={{ padding:'14px', fontSize: 13 }}>{formatNumber(user.usage.creditsUsed)}</div>
                <div style={{ padding:'14px', fontSize: 12, color:'#5a554c' }}>{formatAdminDate(user.usage.lastTranscriptionAt)}</div>
                <div style={{ padding:'12px 14px', display:'flex', gap: 8, alignItems:'center' }}>
                  <input
                    type="number"
                    min="0"
                    step="0.5"
                    max="100000"
                    value={drafts[user.id] ?? String(user.creditsRemaining)}
                    onChange={e => setDrafts(prev => ({ ...prev, [user.id]: e.target.value }))}
                    style={{
                      width: 78,
                      padding:'8px 9px',
                      border:'2px solid var(--ink)',
                      background:'var(--paper)',
                      fontFamily:'JetBrains Mono, monospace',
                      fontSize: 12,
                    }}
                  />
                  <button
                    type="button"
                    onClick={() => saveCredits(user)}
                    disabled={saving === user.id || String(drafts[user.id]) === String(user.creditsRemaining)}
                    style={{
                      border:'2px solid var(--ink)',
                      background:'var(--mint)',
                      padding:'8px 9px',
                      fontSize: 10,
                      fontWeight: 800,
                      letterSpacing:'0.08em',
                      textTransform:'uppercase',
                      opacity: saving === user.id || String(drafts[user.id]) === String(user.creditsRemaining) ? 0.5 : 1,
                    }}
                  >
                    {saving === user.id ? '...' : 'Salva'}
                  </button>
                </div>
              </div>
            ))}

            {!loading && filteredUsers.length === 0 && (
              <div style={{ padding: 22, fontSize: 13, color:'#5a554c' }}>Nessun utente trovato.</div>
            )}
          </div>
        </section>

        <aside style={{ display:'grid', gap: 18 }}>
          <div style={{ background:'var(--paper)', border:'2px solid var(--ink)', boxShadow:'var(--shadow-lg)', padding: 22 }}>
            <div style={{ display:'flex', justifyContent:'space-between', gap: 12, alignItems:'baseline', marginBottom: 14 }}>
              <span style={{ fontSize: 11, fontWeight: 700, letterSpacing:'0.16em', textTransform:'uppercase' }}>Modelli AI</span>
              <span className="mono" style={{ fontSize: 10, color:'#5a554c' }}>Motore voce</span>
            </div>

            <div style={{ display:'grid', gap: 14 }}>
              {modelModes.map(mode => {
                const check = modelCheck?.verification?.find(item => item.mode === mode);
                return (
                  <div key={mode} style={{ display:'grid', gap: 7 }}>
                    <div style={{ display:'flex', justifyContent:'space-between', gap: 12, alignItems:'center' }}>
                      <span style={{ fontSize: 11, fontWeight: 800, letterSpacing:'0.12em', textTransform:'uppercase' }}>{modeLabel(mode)}</span>
                      <span className="mono" style={{ fontSize: 10, color:'#5a554c' }}>
                        attivo
                      </span>
                    </div>
                    <input
                      value={modelDrafts[mode] || ''}
                      onChange={e => setModelDrafts(prev => ({ ...prev, [mode]: e.target.value }))}
                      placeholder={modelDefaults[mode] || 'nome-fornitore/nome-modello'}
                      style={{
                        width:'100%',
                        padding:'10px 11px',
                        border:'2px solid var(--ink)',
                        background:'var(--cream)',
                        fontFamily:'JetBrains Mono, monospace',
                        fontSize: 11,
                        outline:'none',
                      }}
                    />
                    <div style={{ display:'flex', justifyContent:'space-between', gap: 8, alignItems:'center' }}>
                      <span className="mono" style={{ fontSize: 10, color:'#5a554c', overflow:'hidden', textOverflow:'ellipsis', whiteSpace:'nowrap' }}>
                        {modelSettings?.[mode] || modelDefaults[mode] || '—'}
                      </span>
                      {check && (
                        <span style={{
                          flexShrink: 0,
                          padding:'2px 6px',
                          border:'1.5px solid var(--ink)',
                          background: adminModelStatusColor(check.status),
                          fontSize: 9,
                          fontWeight: 800,
                          letterSpacing:'0.06em',
                          textTransform:'uppercase',
                        }}>{adminModelStatusLabel(check.status)}</span>
                      )}
                    </div>
                  </div>
                );
              })}
            </div>

            {modelCheck?.checkedAt && (
              <div className="mono" style={{ fontSize: 10, color:'#5a554c', marginTop: 12 }}>
                Check {formatAdminDate(modelCheck.checkedAt)}
              </div>
            )}

            <div style={{ display:'grid', gridTemplateColumns:'1fr 1fr', gap: 8, marginTop: 16 }}>
              <button
                type="button"
                onClick={verifyModels}
                disabled={modelChecking}
                style={{
                  border:'2px solid var(--ink)',
                  background:'var(--sun)',
                  padding:'10px 8px',
                  fontSize: 10,
                  fontWeight: 800,
                  letterSpacing:'0.08em',
                  textTransform:'uppercase',
                  opacity: modelChecking ? 0.55 : 1,
                }}
              >
                {modelChecking ? 'Check...' : 'Verifica'}
              </button>
              <button
                type="button"
                onClick={saveModels}
                disabled={modelSaving || !modelsChanged}
                style={{
                  border:'2px solid var(--ink)',
                  background:'var(--mint)',
                  padding:'10px 8px',
                  fontSize: 10,
                  fontWeight: 800,
                  letterSpacing:'0.08em',
                  textTransform:'uppercase',
                  opacity: modelSaving || !modelsChanged ? 0.55 : 1,
                }}
              >
                {modelSaving ? 'Salvo...' : 'Salva'}
              </button>
            </div>
          </div>

          <div style={{ background:'var(--paper)', border:'2px solid var(--ink)', boxShadow:'var(--shadow-lg)', padding: 22 }}>
            <div style={{ fontSize: 11, fontWeight: 700, letterSpacing:'0.16em', textTransform:'uppercase', marginBottom: 14 }}>
              Stato utilizzi
            </div>
            <div style={{ display:'grid', gap: 10 }}>
              <AdminMiniRow label="Completate" value={summary.completedTranscriptions} />
              <AdminMiniRow label="In errore" value={summary.failedTranscriptions} />
              <AdminMiniRow label="In corso" value={summary.processingTranscriptions} />
              <AdminMiniRow label="Minuti stimati" value={summary.estimatedMinutes} />
              <AdminMiniRow label="Parole" value={summary.wordsTranscribed} />
            </div>
          </div>

          <div style={{ background:'var(--paper)', border:'2px solid var(--ink)', boxShadow:'var(--shadow-lg)', padding: 22 }}>
            <div style={{ fontSize: 11, fontWeight: 700, letterSpacing:'0.16em', textTransform:'uppercase', marginBottom: 14 }}>
              Ultime trascrizioni
            </div>
            <div style={{ display:'grid', gap: 10 }}>
              {(dashboard?.recentTranscriptions || []).slice(0, 8).map(item => (
                <div key={item.id} style={{ border:'1.5px solid var(--ink)', padding: 10, background:'var(--cream)' }}>
                  <div style={{ display:'flex', justifyContent:'space-between', gap: 8 }}>
                    <strong style={{ fontSize: 12, overflow:'hidden', textOverflow:'ellipsis', whiteSpace:'nowrap' }}>{item.title}</strong>
                    <span className="mono" style={{ fontSize: 10 }}>{item.status}</span>
                  </div>
                  <div style={{ fontSize: 11, color:'#5a554c', marginTop: 4, overflow:'hidden', textOverflow:'ellipsis', whiteSpace:'nowrap' }}>
                    {item.userEmail || 'utente'} · {formatAdminDate(item.createdAt)}
                  </div>
                </div>
              ))}
              {!loading && (dashboard?.recentTranscriptions || []).length === 0 && (
                <div style={{ fontSize: 13, color:'#5a554c' }}>Nessuna trascrizione ancora.</div>
              )}
            </div>
          </div>
        </aside>
      </div>
    </div>
  );
};

const AdminStat = ({ label, value, accent }) => (
  <div style={{ background:'var(--paper)', border:'2px solid var(--ink)', boxShadow:'var(--shadow-sm)', padding: 18 }}>
    <div style={{ width: 28, height: 8, background: accent, border:'1.5px solid var(--ink)', marginBottom: 12 }} />
    <div className="display" style={{ fontSize: 30 }}>{typeof value === 'number' ? formatNumber(value) : value || '0'}</div>
    <div style={{ fontSize: 11, fontWeight: 700, letterSpacing:'0.16em', textTransform:'uppercase', color:'#5a554c', marginTop: 6 }}>
      {label}
    </div>
  </div>
);

const AdminMiniRow = ({ label, value }) => (
  <div style={{ display:'flex', justifyContent:'space-between', gap: 12, fontSize: 13 }}>
    <span style={{ color:'#5a554c' }}>{label}</span>
    <span className="mono" style={{ fontWeight: 700 }}>{formatNumber(value)}</span>
  </div>
);

// ---------- App shell ----------
const Dashboard = ({ user, api, supabaseClient, recoveryMode, onRecoveryHandled, onLogout }) => {
  const [history, setHistory] = React.useState([]);
  const [credits, setCredits] = React.useState(0);
  const [isAdmin, setIsAdmin] = React.useState(false);
  const [view, setView] = React.useState('empty');
  const [activeId, setActiveId] = React.useState(null);
  const [pendingJob, setPendingJob] = React.useState(null);
  const [loading, setLoading] = React.useState(true);
  const [error, setError] = React.useState('');
  const [billingMessage, setBillingMessage] = React.useState('');
  const [billingTone, setBillingTone] = React.useState('info');

  const activeItem = activeId ? history.find(h => h.id === activeId) : null;

  const showBillingMessage = React.useCallback((message, tone = 'info') => {
    setBillingMessage(message);
    setBillingTone(tone);
  }, []);

  React.useEffect(() => {
    if (recoveryMode) {
      setActiveId(null);
      setError('');
      setView('settings');
    }
  }, [recoveryMode]);

  React.useEffect(() => {
    let alive = true;
    async function load() {
      setLoading(true);
      setError('');
      try {
        const checkout = checkoutRedirectState();
        const [me, list] = await Promise.all([api.getMe(), api.listTranscriptions()]);
        if (!alive) return;
        setCredits(me.profile.credits_remaining);
        setIsAdmin(me.profile.is_admin === true);
        const items = (list.transcriptions || []).map(normalizeTranscription);
        setHistory(items);
        if (recoveryMode) {
          setActiveId(null);
          setView('settings');
        } else if (checkout.status === 'success' && checkout.sessionId) {
          setActiveId(null);
          setView('billing');
          try {
            const result = await api.confirmCreditCheckout(checkout.sessionId);
            if (!alive) return;
            if (result.profile?.credits_remaining !== undefined) {
              setCredits(result.profile.credits_remaining);
            }
            if (result.credited) {
              showBillingMessage(`Ricarica completata: +${formatNumber(result.credits)} crediti.`, 'info');
            } else {
              showBillingMessage('Pagamento ricevuto, stiamo aggiornando il saldo.', 'info');
            }
          } catch (error) {
            if (!alive) return;
            showBillingMessage(error.message || 'Non siamo riusciti a confermare la ricarica.', 'error');
          } finally {
            clearCheckoutRedirectState();
          }
        } else if (checkout.status === 'cancel') {
          setActiveId(null);
          setView('billing');
          showBillingMessage('Pagamento annullato. Non hai speso nulla.', 'error');
          clearCheckoutRedirectState();
        } else if (items.length) {
          setActiveId(items[0].id);
          setView('result');
        } else {
          setView('empty');
        }
      } catch (err) {
        if (alive) setError(err.message || 'Errore durante la sincronizzazione.');
      } finally {
        if (alive) setLoading(false);
      }
    }
    load();
    return () => { alive = false; };
  }, [api, recoveryMode]);

  const handleStart = async (job) => {
    setError('');
    setPendingJob(job);
    setView('processing');

    try {
      const result = await api.createTranscription(job);
      let item = normalizeTranscription(result.transcription);
      setPendingJob({ ...job, ...item, file: job.file });
      setCredits(result.profile.credits_remaining);
      setHistory(items => [item, ...items.filter(existing => existing.id !== item.id)]);
      setActiveId(item.id);

      while (item.status === 'processing') {
        await sleep(3500);
        const polled = await api.getTranscription(item.id);
        item = normalizeTranscription(polled.transcription);
        setPendingJob(item);
        setHistory(items => [item, ...items.filter(existing => existing.id !== item.id)]);
      }

      if (item.status === 'error') {
        throw new Error(item.errorMessage || 'Trascrizione non riuscita.');
      }

      setView('result');
    } catch (err) {
      setError(err.message || 'Trascrizione non riuscita.');
      setView('new');
    } finally {
      setPendingJob(null);
    }
  };

  return (
    <div style={{ display:'flex', minHeight:'100vh' }}>
      <Sidebar user={{ ...user, isAdmin }} history={history} activeId={activeId}
        onSelect={(id) => { setActiveId(id); setView('result'); setError(''); }}
        onNew={() => { setActiveId(null); setView('new'); setError(''); }}
        onAdmin={() => { setActiveId(null); setView('admin'); setError(''); }}
        onBilling={() => { setActiveId(null); setView('billing'); setError(''); showBillingMessage('', 'info'); }}
        onSettings={() => { setActiveId(null); setView('settings'); setError(''); }}
        credits={credits} onLogout={onLogout} loading={loading} adminActive={view === 'admin'} billingActive={view === 'billing'} settingsActive={view === 'settings'} />

      <main style={{ flex: 1, minWidth: 0, position: 'relative' }}>
        {loading && <EmptyState onNew={() => setView('new')} error="Caricamento del tuo spazio di lavoro..." />}
        {!loading && view === 'empty' && <EmptyState onNew={() => setView('new')} error={error} />}
        {!loading && view === 'new' && <NewTranscription credits={credits} onCancel={() => setView(activeItem ? 'result' : 'empty')} onStart={handleStart} error={error} />}
        {view === 'processing' && <Processing job={pendingJob} />}
        {!loading && view === 'result' && activeItem && <Result item={activeItem} />}
        {!loading && view === 'billing' && (
          <BillingPanel
            api={api}
            credits={credits}
            message={billingMessage}
            tone={billingTone}
            onMessage={showBillingMessage}
          />
        )}
        {!loading && view === 'admin' && isAdmin && (
          <AdminPanel
            api={api}
            currentUser={user}
            onCreditsChanged={(nextCredits) => setCredits(nextCredits)}
          />
        )}
        {!loading && view === 'settings' && (
          <Settings
            user={user}
            supabaseClient={supabaseClient}
            recoveryMode={recoveryMode}
            onRecoveryHandled={onRecoveryHandled}
          />
        )}
      </main>
    </div>
  );
};

Object.assign(window, { Dashboard, BillingPanel });
