/* global React, Icon, Button, Badge, Card, PageHead, fmtDate, fmtDatetime, qtdTeclaInteira, bloqueioLabel,
          LEITOR_MOTIVOS, LEITOR_MODOS, LEITOR_REPETIDA_MS, leitorCriar, tmLeitor */

// ═══════════════════════════════════════════════════════════════════════════
//  ESTOQUE › LEITOR DE CÓDIGO (CB2, 21/10/2026)
//
//  O leitor USB é um teclado: digita o código no campo que tem o foco e manda
//  Enter. Quatro modos, grandes e na cor, para nunca ler no modo errado:
//    · Identificar — mostra o que o código é (o frasco ou a caixa). Nada grava.
//    · Entrada     — cada leitura SOMA ao estoque (motivo da lista, antes).
//    · Saída       — cada leitura TIRA do estoque; a CAIXA lida aqui sai do
//                    estoque e da caixa, juntas.
//    · Caixa       — tirar da caixa para a prateleira: só a caixa anota; o
//                    estoque NÃO muda (a unidade continua na casa).
//
//  Quem decide é o banco (SUPABASE_codigo-barras-cb2.sql): o porteiro, a lista
//  de motivos, as recusas (desconhecido, dígito errado, caixa bloqueada,
//  granel, OP aberta…) e a mesma leitura nunca gravar duas vezes (o id dela
//  nasce aqui, na hora da leitura, e vai em toda tentativa). A tela só conta
//  o que aconteceu — e o que não aconteceu: toda recusa diz "Nada foi gravado".
//
//  O que a tela faz entre uma leitura e outra mora em `leitorCriar` (db.js),
//  puro, para a PROVA_leitor.js ler "pelo teclado": a quantidade volta a 1, o
//  mesmo código dentro de 0,8 s é o leitor repetindo (ignorado), as gravações
//  saem uma de cada vez e na ordem, e "Desfazer" fica na última gravada.
//
//  O FOCO VOLTA SOZINHO para o campo depois de qualquer clique da tela; sem
//  foco, a faixa diz "clique aqui para voltar a ler" — leitura com o foco em
//  outro lugar vai parar onde o foco estiver.
// ═══════════════════════════════════════════════════════════════════════════

// O som de cada resultado (o depósito é barulhento e a pessoa olha a caixa, não a tela). Sem áudio: a cor e a frase ficam.
let ltrAudio = null;
function ltrSom(tipo) {
  try {
    const AC = window.AudioContext || window.webkitAudioContext;
    if (!AC) return;
    ltrAudio = ltrAudio || new AC();
    // 'repetida' (CB3): o leitor repetindo, ignorado — um toque curto e seco, diferente do certo, para quem conta frascos iguais NOTAR
    const notas = tipo === 'ok' ? [[1046, 0, 0.09]] : tipo === 'atencao' ? [[660, 0, 0.11], [660, 0.16, 0.11]]
      : tipo === 'repetida' ? [[420, 0, 0.05]] : [[240, 0, 0.16], [190, 0.2, 0.26]];
    notas.forEach(([freq, ini, dur]) => {
      const osc = ltrAudio.createOscillator(); const g = ltrAudio.createGain();
      osc.type = tipo === 'erro' ? 'square' : 'sine'; osc.frequency.value = freq; g.gain.value = 0.07;
      osc.connect(g); g.connect(ltrAudio.destination);
      const t0 = ltrAudio.currentTime + ini; osc.start(t0); osc.stop(t0 + dur);
    });
  } catch (e) { /* sem som: a cor e a frase continuam dizendo */ }
}
function ltrSomLido() { try { return window.localStorage.getItem('tm-leitor-som') !== '0'; } catch (e) { return true; } }
function ltrSomGravar(v) { try { window.localStorage.setItem('tm-leitor-som', v ? '1' : '0'); } catch (e) { /* só conveniência */ } }

const ltrNomeItem = (it) => [it.sku, it.nome, it.tom].filter(Boolean).join(' · ');
const ltrHora = (ms) => { try { return new Date(ms).toLocaleTimeString('pt-BR'); } catch (e) { return ''; } };
const ltrModo = (id) => LEITOR_MODOS.find(m => m.id === id) || LEITOR_MODOS[0];

// O cartão do Identificar: o frasco (estoque, caixas, últimas movimentações) ou a caixa (o que resta, as retiradas).
function LtrCartao({ c }) {
  if (!c) return <div className="ltr-cartao vazio">Leia um código para ver o que ele é — o frasco ou a caixa. Nada aqui grava.</div>;
  if (c.tipo === 'lendo') return <div className="ltr-cartao vazio">Lendo <b className="cb-mono">{c.lido}</b>…</div>;
  if (c.tipo === 'teste') return <div className="ltr-cartao ok"><Icon name="check-circle" size={16} /> {c.frase || 'Etiqueta de teste: o leitor está lendo.'}</div>;
  if (c.tipo !== 'itens' && c.tipo !== 'caixa') {
    return <div className="ltr-cartao erro" role="alert"><Icon name="x-circle" size={16} /> {c.frase || ((c.lido || '') + ' não é de nenhum produto, tom ou caixa do cadastro.')}</div>;
  }
  if (c.tipo === 'caixa') {
    const x = c.caixa || {};
    return (
      <div className="ltr-cartao">
        <div className="ltr-cartao-tit"><Icon name="package" size={18} /> Caixa Nº {x.numero} <span className="cb-mono">{x.codigo}</span>
          {x.bloqueio ? <Badge tone="danger">{bloqueioLabel(x.bloqueio)}</Badge> : null}
          {x.esvaziada_em ? <Badge tone="neutral">esvaziada{x.esvaziada_origem === 'retiradas' ? ' pelas retiradas' : ''}</Badge> : null}</div>
        <div className="ltr-cartao-sub">{ltrNomeItem(x)}</div>
        <div className="ltr-numeros">
          <div><b>{x.restam}</b><span>restam pelo leitor</span></div>
          <div><b>{x.unidades}</b><span>un. ao fechar</span></div>
          <div><b>{x.retiradas}</b><span>tiradas pelo leitor</span></div>
        </div>
        <div className="ltr-detalhe">
          Fechada em {fmtDate(x.fechada_em)}{x.responsavel ? ' por ' + x.responsavel : ''}
          {x.lote ? ' · lote ' + x.lote : ''}{x.validade ? ' · validade ' + fmtDate(x.validade) : ''}
        </div>
        {(x.historico || []).length ? (
          <table className="tbl ltr-mini"><thead><tr><th>Quando</th><th>Quem</th><th className="num right">Qtd</th><th>Como</th></tr></thead>
            <tbody>{x.historico.map((h, i) => (
              <tr key={i} className={h.estornada_em ? 'ltr-riscada' : ''}>
                <td>{fmtDatetime(h.quando)}</td><td>{h.quem}</td><td className="num right">{h.quantidade}</td>
                <td>{h.saida ? 'Saída (do estoque e da caixa)' : 'Tirada para a prateleira'}{h.estornada_em ? ' · desfeita' : ''}</td>
              </tr>))}</tbody></table>
        ) : <div className="ltr-detalhe muted">Nada tirado desta caixa pelo leitor.</div>}
      </div>
    );
  }
  const itens = c.itens || [];
  return (
    <div className="ltr-cartao">
      {itens.length > 1 ? (
        <div className="cl-aviso warn"><Icon name="alert-triangle" size={14} /><span>O código <b className="cb-mono">{c.lido}</b> está em {itens.length} itens — é um EAN repetido. O dono se escolhe em Produtos › Códigos de barras; até lá, Entrada e Saída perguntam qual é.</span></div>
      ) : null}
      {itens.map((it, i) => (
        <div key={i} className="ltr-item">
          <div className="ltr-cartao-tit"><Icon name="package-check" size={18} /> {it.nome}{it.tom ? ' · ' + it.tom : ''} <span className="cb-mono">{it.sku}</span></div>
          {it.tem_tons ? <div className="cl-aviso warn"><Icon name="palette" size={14} /><span>Produto com tonalidades: o estoque é a soma dos tons. Para Entrada e Saída, leia o código do TOM (ou escolha o tom quando o Leitor perguntar).</span></div> : null}
          <div className="ltr-numeros">
            <div><b>{it.estoque}</b><span>em estoque{it.bloqueado ? ` (${it.bloqueado} bloqueado${it.bloqueado > 1 ? 's' : ''})` : ''}</span></div>
            <div><b>{it.minimo || 0}</b><span>mínimo</span></div>
            <div><b>{(it.caixas || []).length}</b><span>caixa{(it.caixas || []).length === 1 ? '' : 's'} aberta{(it.caixas || []).length === 1 ? '' : 's'}</span></div>
          </div>
          <div className="ltr-detalhe">
            {it.ean ? <>EAN <span className="cb-mono">{it.ean}</span> · </> : null}
            {it.codigo_interno ? <>código interno <span className="cb-mono">{it.codigo_interno}</span> · </> : null}
            {it.unidade || 'UN'}{it.status ? ' · ' + it.status : ''}
          </div>
          {(it.caixas || []).length ? (
            <div className="ltr-caixas">{it.caixas.map(cx => (
              <span key={cx.codigo} className={'ltr-cx' + (cx.bloqueio ? ' bloq' : '')}>
                Nº {cx.numero} <span className="cb-mono">{cx.codigo}</span> · {cx.restam} de {cx.unidades}{cx.bloqueio ? ' · ' + bloqueioLabel(cx.bloqueio) : ''}
              </span>))}</div>
          ) : null}
          {(it.ultimas || []).length ? (
            <table className="tbl ltr-mini"><thead><tr><th>Quando</th><th>Tipo</th><th>Motivo</th><th className="num right">Qtd</th><th className="num right">Saldo</th><th>Quem</th></tr></thead>
              <tbody>{it.ultimas.map((m, k) => (
                <tr key={k}><td>{fmtDatetime(m.quando)}</td><td>{m.tipo}</td><td className="trunca" title={m.motivo || ''}>{m.motivo || '—'}</td>
                  <td className="num right">{m.quantidade > 0 ? '+' + m.quantidade : m.quantidade}</td><td className="num right">{m.saldo_apos}</td><td>{m.responsavel}</td></tr>))}</tbody></table>
          ) : <div className="ltr-detalhe muted">Sem movimentação registrada.</div>}
        </div>
      ))}
    </div>
  );
}

// O que uma leitura gravada FEZ, numa frase (o antes → depois do estoque; o que resta na caixa).
function ltrFeito(l) {
  const d = l.r || {};
  const it = d.item || {};
  const cx = d.caixa;
  const partes = [];
  if (l.modo === 'caixa') {
    partes.push(`tirou ${l.qtd} da caixa Nº ${cx ? cx.numero : '?'}`);
    if (cx) partes.push(cx.esvaziada_em ? 'a caixa ACABOU (esvaziada pelas retiradas)' : `restam ${cx.restam} de ${cx.unidades}`);
    partes.push('estoque não mudou (' + it.estoque + ')');
  } else {
    const mv = d.mov || {};
    partes.push(`estoque ${mv.antes} → ${mv.saldo_apos}`);
    if (cx) partes.push(`caixa Nº ${cx.numero}: ${cx.esvaziada_em ? 'acabou' : 'restam ' + cx.restam + ' de ' + cx.unidades}`);
  }
  if (l.jaExistia) partes.push('já estava gravada (a primeira tentativa tinha chegado)');
  return partes.join(' · ');
}

function LtrLinha({ l, ultima, onDesfazer, onTentar }) {
  const modo = ltrModo(l.modo);
  const d = l.r || {};
  const it = d.item || null;
  const sinal = l.modo === 'entrada' ? '+' : l.modo === 'saida' ? '−' : '';
  const estado = {
    enviando: ['Gravando…', 'neutral'], gravado: ['Gravada', 'success'], desfeita: ['Desfeita', 'neutral'], escolher: ['Escolha o item', 'warning'],
    recusado: ['Não gravada', 'danger'], sem_resposta: ['Sem resposta', 'warning'], cancelado: ['Não gravada', 'neutral'], teste: ['Teste', 'info'],
  }[l.estado] || [l.estado, 'neutral'];
  return (
    <tr className={'ltr-linha ' + l.estado}>
      <td className="ltr-hora">{ltrHora(l.quando)}</td>
      <td><span className={'ltr-tag ltr-' + l.modo}>{modo.rotulo}</span></td>
      <td className="num right ltr-qtd">{sinal}{l.qtd}</td>
      <td>
        <div className="ltr-o-que">{it ? ltrNomeItem(it) : <span className="cb-mono">{l.codigo}</span>}</div>
        <div className="ltr-sub">
          {it ? <span className="cb-mono">{l.codigo}</span> : null}
          {l.motivo ? <span>{l.motivo}</span> : null}
          {l.estado === 'gravado' ? <span className="ltr-feito">{ltrFeito(l)}</span> : null}
          {l.estado === 'desfeita' ? <span className="ltr-feito">desfeita{l.rDesfeita && l.rDesfeita.estorno ? ` · estoque voltou a ${l.rDesfeita.estorno.saldo_apos}` : (l.modo === 'caixa' ? ' · a retirada saiu da caixa' : '')}</span> : null}
        </div>
        {l.frase ? <div className={'ltr-frase' + (l.estado === 'recusado' ? ' erro' : '')} role={l.estado === 'recusado' ? 'alert' : undefined}>{l.frase}</div> : null}
        {l.fraseDesfazer ? <div className="ltr-frase erro" role="alert">{l.fraseDesfazer}</div> : null}
      </td>
      <td><Badge tone={estado[1]}>{estado[0]}</Badge></td>
      <td className="ltr-acao">
        {l.estado === 'sem_resposta' ? (
          <Button size="sm" variant="secondary" icon="rotate-cw" pede={l.modo === 'caixa' ? 'criar' : ['criar', 'editar']} onClick={() => onTentar(l.id)}>Tentar de novo</Button>
        ) : null}
        {ultima ? (
          <Button size="sm" variant="ghost" icon="undo-2" pede={l.modo === 'caixa' ? 'criar' : ['criar', 'editar']} disabled={!!l.desfazendo}
            title="Desfaz esta leitura: o estoque volta (um Ajuste com estorno) e, se veio da caixa, a retirada sai dela"
            onClick={() => onDesfazer(l.id)}>{l.desfazendo ? 'Desfazendo…' : 'Desfazer'}</Button>
        ) : null}
      </td>
    </tr>
  );
}

function EstoqueLeitor({ onNav }) {
  const [, setVersao] = React.useState(0);
  const [focado, setFocado] = React.useState(false);
  const [som, setSom] = React.useState(ltrSomLido);
  const somRef = React.useRef(som);
  somRef.current = som;
  const campoRef = React.useRef(null);
  const ctlRef = React.useRef(null);
  if (!ctlRef.current) {
    ctlRef.current = leitorCriar({
      identificar: (codigo) => tmLeitor.identificar(codigo),
      registrar: (a) => tmLeitor.registrar(a),
      desfazer: (id) => tmLeitor.desfazer(id),
      // o handler pergunta pelo perfil: o Enter do leitor não passa por botão nenhum
      negado: (modo) => { const m = ltrModo(modo); return m.pede ? (window.tmNegadoAqui ? window.tmNegadoAqui(m.pede) || '' : 'A checagem de permissão não carregou — recarregue a página.') : ''; },
      confirmar: (frase) => window.confirm(frase),
      avisar: (tipo) => { if (somRef.current) ltrSom(tipo); },
      mudou: () => setVersao(v => v + 1),
    });
  }
  const ctl = ctlRef.current;
  const s = ctl.s;
  const voltar = () => setTimeout(() => { if (campoRef.current) campoRef.current.focus(); }, 0);
  React.useEffect(() => { voltar(); }, []);  // eslint-disable-line
  // A tecla que chega com o foco FORA dos campos (um clique no vazio, um botão) é do leitor: vai para o campo. E o Enter
  // dela não "clica" o botão que estiver com o foco (o Desfazer!) — a escuta é na captura, antes do botão. Os campos de
  // verdade (a quantidade, o motivo) ficam com as teclas deles.
  React.useEffect(() => {
    const naTecla = (e) => {
      const alvo = e.target; const tag = alvo && alvo.tagName;
      if (alvo === campoRef.current || tag === 'INPUT' || tag === 'SELECT' || tag === 'TEXTAREA' || e.ctrlKey || e.metaKey || e.altKey) return;
      const c = ctlRef.current;
      if (e.key === 'Enter') { if (c.s.texto) { e.preventDefault(); c.enter(); voltar(); } return; }
      if (e.key && e.key.length === 1 && e.key !== ' ') { e.preventDefault(); c.digitar(c.s.texto + e.key); voltar(); }
    };
    document.addEventListener('keydown', naTecla, true);
    return () => document.removeEventListener('keydown', naTecla, true);
  }, []);  // eslint-disable-line

  const modo = ltrModo(s.modo);
  const pendente = ctl.escolhaPendente();
  const ultima = ctl.ultimaDesfazivel();
  const gravadas = s.fila.filter(l => l.estado === 'gravado').length;

  const tecla = (e) => {
    if (e.key === 'Enter' || (e.key === 'Tab' && s.texto)) { e.preventDefault(); ctl.enter(); }
  };

  return (
    <div className={'canvas ltr-tela ltr-em-' + s.modo}>
      <PageHead title="Leitor de código"
        sub="O leitor é um teclado: deixe o cursor no campo grande e leia. Identificar só mostra; Entrada e Saída mexem no estoque; Caixa só anota o que saiu da caixa."
        right={<label className="ltr-som"><input type="checkbox" checked={som} onChange={e => { setSom(e.target.checked); ltrSomGravar(e.target.checked); voltar(); }} /> Som</label>} />

      <div className="ltr-modos" role="radiogroup" aria-label="Modo do leitor">
        {LEITOR_MODOS.map(m => {
          const base = { onClick: () => { ctl.setModo(m.id); voltar(); } };
          const props = m.pede ? window.tmPedeProps(m.pede, base) : base;
          return (
            <button key={m.id} type="button" role="radio" aria-checked={s.modo === m.id}
              className={'ltr-modo ltr-' + m.id + (s.modo === m.id ? ' on' : '')} {...props}>
              <Icon name={m.icone} size={22} />
              <span className="ltr-modo-nome">{m.rotulo}</span>
              <span className="ltr-modo-frase">{m.frase}</span>
            </button>
          );
        })}
      </div>

      <div className={'ltr-faixa ltr-' + s.modo}>
        <Icon name={modo.icone} size={26} />
        <div>
          <div className="ltr-faixa-nome">{modo.rotulo.toUpperCase()}</div>
          <div className="ltr-faixa-frase">{modo.frase}</div>
        </div>
        {s.modo === 'entrada' || s.modo === 'saida' ? (
          <label className="ltr-campo-pq">
            <span>Motivo da {s.modo === 'entrada' ? 'entrada' : 'saída'}</span>
            <select className="input" value={s.motivo} onChange={e => { ctl.setMotivo(e.target.value); voltar(); }}>
              <option value="">Escolha o motivo…</option>
              {LEITOR_MOTIVOS[s.modo].map(m => <option key={m} value={m}>{m}</option>)}
            </select>
          </label>
        ) : null}
        {s.modo !== 'identificar' ? (
          <label className="ltr-campo-pq ltr-qtd-campo">
            <span>Quantidade por leitura</span>
            <input className="input" inputMode="numeric" value={s.qtd} onChange={e => ctl.setQtd(e.target.value)}
              onKeyDown={e => { qtdTeclaInteira(e); if (e.key === 'Enter') { e.preventDefault(); voltar(); } }}
              onFocus={e => e.target.select()} aria-label="Quantidade por leitura" />
            <small>volta a 1 depois de cada leitura</small>
          </label>
        ) : null}
      </div>

      <div className={'ltr-leitura' + (focado ? ' pronto' : '')}>
        <label className="ltr-campo">
          <Icon name="scan-line" size={24} />
          <input ref={campoRef} value={s.texto} onChange={e => ctl.digitar(e.target.value)} onKeyDown={tecla}
            onFocus={() => setFocado(true)} onBlur={() => setFocado(false)}
            autoComplete="off" spellCheck={false} aria-label="Código lido"
            placeholder="Leia o código com o leitor (ou digite e tecle Enter)" />
        </label>
        {focado ? (
          <div className="ltr-status pronto"><Icon name="radio" size={14} /> PRONTO PARA LER — {modo.rotulo}{s.modo === 'entrada' || s.modo === 'saida' ? (s.motivo ? ' · ' + s.motivo : ' · escolha o motivo') : ''}
            {s.modo !== 'identificar' && s.qtd !== '1' ? <span className="ltr-proxima">próxima leitura: {s.qtd || '?'}</span> : null}</div>
        ) : (
          <button type="button" className="ltr-status parado" onClick={voltar}><Icon name="mouse-pointer-click" size={14} /> CLIQUE AQUI PARA VOLTAR A LER — sem o cursor no campo, a leitura vai para outro lugar</button>
        )}
        {s.repetidas ? <div className="ltr-nota">{s.repetidas} leitura{s.repetidas > 1 ? 's' : ''} repetida{s.repetidas > 1 ? 's' : ''} ignorada{s.repetidas > 1 ? 's' : ''} (o mesmo código em menos de {LEITOR_REPETIDA_MS / 1000} s é o leitor repetindo). Para várias unidades, use a quantidade.</div> : null}
      </div>

      {pendente ? (
        <Card padded title={'Qual item é? · ' + pendente.codigo}>
          <div className="ltr-escolher">
            <div className="ltr-nota">Este código é de mais de um item (um EAN repetido, ou o produto com tonalidades). Escolha o certo — nada foi gravado ainda.</div>
            {(pendente.itens || []).map((it, i) => (
              <button key={i} type="button" className="ltr-opcao"
                {...window.tmPedeProps(pendente.modo === 'caixa' ? 'criar' : ['criar', 'editar'], { onClick: () => { ctl.escolher(pendente.id, it); voltar(); } })}>
                <Icon name="check" size={14} /> {ltrNomeItem(it)}
              </button>
            ))}
            <Button variant="ghost" size="sm" onClick={() => { ctl.cancelarEscolha(pendente.id); voltar(); }}>Nenhum destes (não gravar)</Button>
          </div>
        </Card>
      ) : null}

      {s.modo === 'identificar' ? (
        <Card padded title="O que é"><LtrCartao c={s.cartao} /></Card>
      ) : null}

      <Card padded title={'Leituras desta sessão' + (s.fila.length ? ` · ${gravadas} gravada${gravadas === 1 ? '' : 's'}` : '')}>
        {s.fila.length === 0 ? (
          <div className="ltr-vazio">Nenhuma leitura ainda. Cada leitura aparece aqui, com o que ela fez — ou por que não gravou.
            Só a última gravada pode ser desfeita, por quem leu.</div>
        ) : (
          <table className="tbl ltr-fila">
            <colgroup><col style={{ width: 80 }} /><col style={{ width: 96 }} /><col style={{ width: 64 }} /><col /><col style={{ width: 128 }} /><col style={{ width: 150 }} /></colgroup>
            <thead><tr><th>Hora</th><th>Modo</th><th className="num right">Qtd</th><th>Item e resultado</th><th>Situação</th><th></th></tr></thead>
            <tbody>
              {s.fila.map(l => (
                <LtrLinha key={l.id} l={l} ultima={ultima && ultima.id === l.id}
                  onDesfazer={(id) => { ctl.desfazer(id); voltar(); }} onTentar={(id) => { ctl.tentarDeNovo(id); voltar(); }} />
              ))}
            </tbody>
          </table>
        )}
      </Card>
    </div>
  );
}

Object.assign(window, { EstoqueLeitor, LtrCartao, LtrLinha, ltrFeito, ltrSom, ltrSomLido, ltrSomGravar });
