/* Acciones del ticket · Pieza 3a
   Las acciones NO se inventan por pantalla: salen de la tabla canónica de
   tsm-config.js — TSM_ACCIONES[patrón][estado] → [[callback, label], …].
   Cada patrón tiene su propia máquina, así que la misma pantalla ofrece
   distinto según el rubro. Acá está copiada verbatim.

   HALLAZGO: no existe TSM_REPROGRAMAR en ninguno de los 7 patrones. Los
   mensajes de CANCELADO y AUSENTE dicen "te va a contactar para reprogramar",
   o sea que pasar un turno hoy se resuelve por fuera del sistema y el ticket
   queda cancelado. Por eso la alerta "Paula pidió pasar el turno" no tenía
   dónde caer. Se propone abajo como acción nueva.                          */

const TSM_ACCIONES = {
  A:{ CREADO:[['CONFIRMADO','Confirmar'],['CANCELADO','Cancelar']],
      CONFIRMADO:[['EN_CAMINO','En camino'],['CANCELADO','Cancelar']],
      EN_CAMINO:[['FINALIZADO','Finalizado'],['DEMORADO','Me demoré'],['AUSENTE','No estaba']],
      DEMORADO:[['EN_CAMINO','Estoy yendo'],['FINALIZADO','Finalizado']] },
  B:{ CREADO:[['CONFIRMADO','Confirmar'],['CANCELADO','Cancelar']],
      CONFIRMADO:[['FINALIZADO','Finalizado'],['DEMORADO','Demorado'],['AUSENTE','No vino'],['CANCELADO','Cancelar']],
      DEMORADO:[['FINALIZADO','Finalizado'],['AUSENTE','No vino']] },
  C:{ CREADO:[['CONFIRMADO','Confirmar'],['CANCELADO','Cancelar']],
      CONFIRMADO:[['EN_CAMINO','En camino'],['CANCELADO','Cancelar']],
      EN_CAMINO:[['FINALIZADO','Finalizado'],['DEMORADO','Me demoré'],['AUSENTE','No estaba']],
      DEMORADO:[['EN_CAMINO','Estoy yendo'],['FINALIZADO','Finalizado']] },
  D:{ CREADO:[['CONFIRMADO','Confirmar'],['CANCELADO','Cancelar']],
      CONFIRMADO:[['EN_PREPARACION','En preparación'],['CANCELADO','Cancelar']],
      EN_PREPARACION:[['EN_CAMINO','En camino'],['CANCELADO','Cancelar']],
      EN_CAMINO:[['ENTREGADO','Entregado'],['DEMORADO','Demorado'],['CANCELADO','Cancelar']],
      DEMORADO:[['EN_CAMINO','Estoy yendo'],['ENTREGADO','Entregado']] },
  E:{ CREADO:[['CONFIRMADO','Confirmar'],['CANCELADO','Cancelar']],
      CONFIRMADO:[['EN_CAMINO','En camino'],['CANCELADO','Cancelar']],
      EN_CAMINO:[['EN_CURSO','En curso'],['CANCELADO','Cancelar']],
      EN_CURSO:[['FINALIZADO','Finalizado'],['CANCELADO','Cancelar']] },
  F:{ CREADO:[['CONFIRMADO','Confirmar'],['CANCELADO','Cancelar']],
      CONFIRMADO:[['FINALIZADO','Finalizado'],['DEMORADO','Demorado'],['AUSENTE','No vino'],['CANCELADO','Cancelar']],
      DEMORADO:[['FINALIZADO','Finalizado'],['AUSENTE','No vino']] },
  G:{ CREADO:[['RECIBIDO','Recibido'],['CANCELADO','Cancelar']],
      RECIBIDO:[['EN_REPARACION','En reparación'],['CANCELADO','Cancelar']],
      EN_REPARACION:[['LISTO_PARA_RETIRAR','Listo p/ retirar'],['CANCELADO','Cancelar']],
      LISTO_PARA_RETIRAR:[['RETIRADO','Retirado'],['CANCELADO','Cancelar']] },
};

/* Cada rubro le dice distinto a lo mismo. Un plomero reagenda una VISITA, una
   peluquera reagenda un TURNO, un taller no reagenda nada: se retira. Usar
   "turno" para los siete es hablarle en un idioma que no es el suyo.       */
const VOZ = {
  A:{ ev:'la visita', evc:'Visita', ir:true  },
  B:{ ev:'el turno',  evc:'Turno',  ir:false },
  C:{ ev:'la visita', evc:'Visita', ir:true  },
  D:{ ev:'la entrega',evc:'Entrega',ir:true  },
  E:{ ev:'el evento', evc:'Evento', ir:true  },
  F:{ ev:'la consulta',evc:'Consulta',ir:false },
  G:{ ev:'la entrada',evc:'Entrada',ir:false },
};

/* Acciones que el TSM NO tiene y el trabajo real necesita. Van marcadas para
   que Dev sepa que hay que agregarlas a tsm-config.js, no son un olvido:
     REAGENDAR   — mover el evento sin cancelar el ticket.
     RELEVADO    — fui, vi, falta presupuestar. El TSM solo permite FINALIZADO
                   o CANCELADO: no existe "la visita terminó pero el trabajo no".
     A_PRESUPUESTAR — relevado + coordinar la vuelta, en un paso.            */
const PROPUESTAS = {
  REAGENDAR:      { lbl:v => `Reagendar ${v.ev}`,                     wa:v => `Te propongo pasar ${v.ev} a otro horario. ¿Te sirve?`, tono:'v' },
  RELEVADO:       { lbl:() => 'Fui, falta presupuestar',              wa:() => 'Ya vi lo que hay que hacer. Te paso el presupuesto en un rato.', tono:'a' },
  A_PRESUPUESTAR: { lbl:v => `Presupuestar y coordinar otra ${v.evc.toLowerCase()}`, wa:v => `Te paso el presupuesto y coordinamos ${v.ev} para hacerlo.`, tono:'a' },
};

/* ¿Este toque le habla al cliente? No alcanza mirar el paso DESTINO: un atajo
   a "Llegué y arranqué" salta desde el paso actual y CRUZA "En camino", que
   sí manda WhatsApp. Hay que clasificar por todos los peldaños que el salto
   atraviesa. Las tres superficies llaman a esta función para que no divergan. */
function saltoHabla(pasos, desde, hasta) {
  return pasos.slice(desde + 1, hasta + 1).some(k => !!MSG_WA[k] && k !== 'AGENDADO');
}

/* ── confirmación de acciones que le hablan al cliente ────────
   Todo lo que dispara un WhatsApp real es IRREVERSIBLE: el mensaje ya salió.
   Un toque accidental en "Llegué y arranqué" le avisa al cliente que estás en
   su casa cuando no estás. Estos botones piden dos toques: el primero arma y
   muestra qué se va a mandar, el segundo manda. Se desarma solo a los 4 s y
   con Escape, así que ignorarlo también cancela.
   Los pasos internos —los que no le llegan al cliente— siguen a un toque. */
function useArmar(ms = 4000) {
  const [armado, setArmado] = React.useState(null);
  const t = React.useRef(null);
  React.useEffect(() => {
    if (armado == null) return;
    t.current = window.setTimeout(() => setArmado(null), ms);
    const esc = e => e.key === 'Escape' && setArmado(null);
    window.addEventListener('keydown', esc);
    return () => { window.clearTimeout(t.current); window.removeEventListener('keydown', esc); };
  }, [armado, ms]);
  /* pedir(id, fn): primer toque arma, segundo ejecuta. */
  return [armado, (id, fn) => { if (armado === id) { setArmado(null); fn(); } else setArmado(id); }, () => setArmado(null)];
}

const EST_LBL = {
  CREADO:'Sin confirmar', CONFIRMADO:'Confirmado', EN_CAMINO:'En camino',
  EN_PREPARACION:'En preparación', EN_CURSO:'En curso', EN_REPARACION:'En reparación',
  RECIBIDO:'Recibido', LISTO_PARA_RETIRAR:'Listo para retirar', DEMORADO:'Demorado',
  AUSENTE:'No estaba', RELEVADO:'Relevado · a presupuestar', FINALIZADO:'Finalizado', ENTREGADO:'Entregado',
  RETIRADO:'Retirado', CANCELADO:'Cancelado',
};
const EST_TONO = {
  CANCELADO:'coral', AUSENTE:'coral', DEMORADO:'amber', CREADO:'amber', RELEVADO:'amber',
  FINALIZADO:'green', ENTREGADO:'green', RETIRADO:'green', LISTO_PARA_RETIRAR:'green',
};
/* Evidencia: un paso está hecho si el dato existe, lo haya escrito quien lo
   haya escrito. Vivía sólo en el camino de escritura, así que los trabajos
   que ya venían con presupuesto cargado y enviado se dibujaban sin tildar,
   contradiciendo al documento y al botón de la misma pantalla.          */
const evidencia = (j, k) => k === 'PRESUPUESTADO' ? !!(j.items && j.items.length)
  : k === 'ENVIADO' ? !!j.enviado
  : k === 'APROBADO' ? !!j.enviado && esCierre(j.tsm)
  : false;

/* ── bitácora ───────────────────────────────────────────────
   La escalera de pasos dice DÓNDE está el trabajo; la bitácora dice CÓMO
   llegó. Son cosas distintas y hasta ahora sólo existía la primera: una
   reagenda no dejaba rastro, un "en camino" que después se canceló
   desaparecía, y el ticket terminaba mintiendo sobre su propia historia.
   Es append-only —los eventos no se pisan— y de acá salen las métricas que
   hoy no se pueden calcular: cuántas veces se reprograma un turno, cuánto
   tarda un presupuesto en aprobarse, qué rubro cancela más.             */
let RELOJ = 9 * 60 + 12;
const hora = () => { RELOJ += 7 + Math.floor(Math.random() * 34);
  return `${String(Math.floor(RELOJ / 60) % 24).padStart(2,'0')}:${String(RELOJ % 60).padStart(2,'0')}`; };
const anotar = (j, ev) => ({ ...j, bit:[...(j.bit || []), { h:hora(), ...ev }] });
/* Lazy: `T` vive en pieza3-miniapp.jsx, que carga DESPUÉS de este archivo.
   Tocarlo en el tope del módulo abortaba el script entero y dejaba sin definir
   todo lo declarado más abajo (esCierre, accionesDe…). El resto del archivo ya
   usa `T` sólo dentro de funciones; esto se alinea. */
const tonoEv = k => ({ alta:T.v600, mia:T.v600, vos:T.c300, mal:T.coral, ok:T.green, cambio:T.amber }[k]);

function Bitacora({ job, compacto }) {
  const b = job.bit || [];
  if (!b.length) return null;
  return (
    <Card pad={0} gap={0} style={{ overflow:'hidden' }}>
      <div style={{ display:'flex', alignItems:'baseline', gap:10, padding:'14px 14px 10px' }}>
        <Lbl>CÓMO VINO ESTE TRABAJO</Lbl>
        <span style={{ marginLeft:'auto', fontFamily:T.mono, fontSize:9, color:T.c500 }}>{b.length} {b.length === 1 ? 'MOVIMIENTO' : 'MOVIMIENTOS'}</span>
      </div>
      <div style={{ padding:'0 14px 14px' }}>
        {b.map((e,i) => (
          <div key={i} style={{ display:'flex', gap:11, alignItems:'flex-start', paddingBottom:i === b.length - 1 ? 0 : 10 }}>
            <span style={{ width:36, flex:'none', fontFamily:T.mono, fontSize:9.5, color:T.c500, paddingTop:2, textAlign:'right' }}>{e.h}</span>
            <span style={{ width:9, flex:'none', display:'flex', flexDirection:'column', alignItems:'center', alignSelf:'stretch' }}>
              <i style={{ width:7, height:7, borderRadius:9, background:tonoEv(e.tono) || T.c500, display:'block', marginTop:5, flex:'none' }} />
              {i < b.length - 1 && <i style={{ width:1, flex:1, background:T.g800, display:'block' }} />}
            </span>
            <span style={{ flex:1, minWidth:0, paddingBottom:2 }}>
              <span style={{ display:'block', fontFamily:T.sans, fontSize:13, lineHeight:1.4, color:T.c100 }}>{e.t}</span>
              {e.d && <span style={{ display:'block', fontFamily:T.sans, fontSize:11.5, lineHeight:1.4, color:T.c500, marginTop:2 }}>{e.d}</span>}
            </span>
            <span style={{ flex:'none', fontFamily:T.mono, fontSize:8, letterSpacing:'.1em', color:e.por === 'mia' ? T.c100 : T.c500, paddingTop:3 }}>{e.por === 'mia' ? 'MIA' : e.por === 'cli' ? 'CLIENTE' : 'VOS'}</span>
          </div>
        ))}
      </div>
    </Card>
  );
}

/* Métricas que sólo existen porque hay bitácora. */
const metricasBit = jobs => {
  const ev = jobs.flatMap(j => j.bit || []);
  return {
    reagendas: ev.filter(e => e.tipo === 'reagenda').length,
    cancelados: jobs.filter(j => j.tsm === 'CANCELADO').length,
    demoras: ev.filter(e => e.tipo === 'demora').length,
    movs: ev.length,
  };
};

/* Reagendar toca el TURNO, no el trabajo entero. Antes reseteaba la escalera
   a cero, así que un presupuesto ya elaborado y enviado se destildaba solo
   porque el cliente pidió otro horario. Se limpian los peldaños de la visita
   —confirmación, en camino, arranque— y se conserva lo que tiene evidencia
   propia: los ítems cargados y el envío siguen siendo ciertos. */
const RUNGS_VISITA = ['CONFIRMADO','EN_CAMINO','COMENZADO','EN_CURSO','EN_REPARACION','EN_PREPARACION','FINALIZADO','ENTREGADO','RETIRADO','REALIZADO','COBRADO'];
/* No es lo mismo reagendar porque el cliente no podía que porque fuiste, viste
   lo que hay que hacer y falta presupuestar. En el primer caso la visita NO
   pasó y sus peldaños se limpian; en el segundo SÍ pasó —fuiste, relevaste— y
   borrarla sería mentir. El motivo decide qué se conserva. */
const MOTIVOS = [
  { k:'relevado', t:'Ya fui · falta presupuestar', d:'La visita cuenta como hecha. Queda el presupuesto y una segunda visita para hacer el trabajo.', mantiene:true },
  { k:'material', t:'Me faltó un material',        d:'Volvés a terminarlo. La visita queda registrada.', mantiene:true },
  { k:'cliente',  t:'El cliente no podía',         d:'La visita no pasó. El turno arranca de cero y Mia le pide que confirme el nuevo.', mantiene:false },
  { k:'vos',      t:'No pude ir',                  d:'La visita no pasó. Mia le avisa y le propone el horario nuevo.', mantiene:false },
];
function trasReagenda(j, pasos, motivo) {
  const mot = MOTIVOS.find(x => x.k === motivo) || MOTIVOS[2];
  /* Si la visita pasó, se conservan sus peldaños y sólo vuelve a quedar
     pendiente el cierre del trabajo; si no pasó, se limpian. En los dos casos
     lo que tiene evidencia propia —ítems cargados, presupuesto enviado— queda
     como está: no depende del turno. */
  const CIERRA = ['FINALIZADO','ENTREGADO','RETIRADO','REALIZADO','COBRADO'];
  const h = pasos.map((k,i) => {
    if (i === 0) return (j.hist || [])[0] || 'mia';
    if (CIERRA.includes(k)) return null;
    if (RUNGS_VISITA.includes(k)) return mot.mantiene ? ((j.hist || [])[i] || null) : null;
    return evidencia(j, k) ? ((j.hist || [])[i] || 'vos') : null;
  });
  let p = 0; while (h[p + 1]) p++;
  return { hist:h, paso:p, esperaCli:!mot.mantiene, motivo:mot.k };
}

const CIERRE = ['FINALIZADO','ENTREGADO','RETIRADO','CANCELADO'];
const esCierre = e => CIERRE.includes(e);
/* Cobrado es una lista EXPLÍCITA, no "todo lo cerrado menos cancelado": esa
   exclusión absorbe en silencio cualquier estado terminal futuro que no sea
   plata. RELEVADO —fui, falta presupuestar— no cierra el ticket ni entra acá:
   el trabajo sigue abierto, esperando el presupuesto.                      */
const COBRADOS = ['FINALIZADO','ENTREGADO','RETIRADO'];
const esCobrado = e => COBRADOS.includes(e);
/* Lo que Mia le dice al cliente. Se muestra ANTES de tocar, porque el tap
   dispara un WhatsApp real y el emprendedor tiene que saber qué se manda. */
const MSG_WA = {
  CONFIRMADO:'Te confirmo el turno. ¡Nos vemos!',
  EN_CAMINO:'Ya estoy en camino, en breve llego.',
  EN_PREPARACION:'Estoy preparando tu pedido, te aviso cuando salga.',
  EN_CURSO:'¡Ya arrancamos!',
  EN_REPARACION:'Tu equipo entró en reparación, te aviso con novedades.',
  RECIBIDO:'Recibimos tu equipo. Te aviso con novedades.',
  LISTO_PARA_RETIRAR:'¡Tu equipo está listo para retirar!',
  DEMORADO:'Voy a llegar un poco más tarde de lo previsto.',
  FINALIZADO:'¡Listo! Quedó terminado. Gracias por confiar.',
  ENTREGADO:'Tu pedido fue entregado. ¡Gracias!',
  RETIRADO:'¡Equipo retirado! Gracias por confiar.',
  AUSENTE:'Pasé pero no encontré a nadie. Te contacto para reprogramar.',
  CANCELADO:'Tuve que cancelar. ¿Querés que busquemos otra fecha, o preferís dejarlo? Contame así lo anoto.',
  REPROGRAMADO:'Te paso el turno para el nuevo horario. ¿Te sirve?',
};

const accionesDe = (patron, est) => (TSM_ACCIONES[patron] || TSM_ACCIONES.C)[est] || [];

/* ── hoja de acciones ─────────────────────────────────────── */
function SheetAcc({ job, patron, onClose, onAccion, onPasar, onRelevar }) {
  /* Cancelar no es un tap: casi siempre hay una fecha nueva atrás. Antes de
     cerrar el ticket Mate pregunta si se puede reagendar, y si igual se
     cancela, Mia le pide el motivo al cliente — sin eso el trabajo se pierde
     sin dato y el reporte no puede decir por qué se cae. */
  const [confirmaCancel, setConfirmaCancel] = React.useState(false);
  const acc = accionesDe(patron, job.tsm);
  const v = VOZ[patron] || VOZ.C;
  const abierto = !esCierre(job.tsm);
  const puedeReagendar = abierto && job.hora && job.hora !== '—';
  /* Relevar y presupuestar salieron de acá: la escalera de estados ya tiene
     "Elaborar el presupuesto" con su ABRIR →, y tenerlo en dos lugares era la
     duplicación que veníamos limpiando. Queda sólo reagendar, que no vive
     en ningún otro lado.                                                */
  const acc2 = job.tsm === 'RELEVADO' ? [['FINALIZADO','Finalizado'],['CANCELADO','Cancelar']] : acc;
  const prop = (k, on) => { const p = PROPUESTAS[k], col = p.tono === 'a' ? T.amber : T.v600, bg = p.tono === 'a' ? T.aSoft : T.vSoft, bd = p.tono === 'a' ? T.aBorder : T.vBorder;
    return (
      <button key={k} onClick={on} style={{ display:'flex', flexDirection:'column', alignItems:'flex-start', gap:4, flexShrink:0, padding:'12px 14px', background:bg, border:`1px solid ${bd}`, borderRadius:12, cursor:'pointer', textAlign:'left', width:'100%' }}>
        <span style={{ display:'flex', alignItems:'center', gap:7, width:'100%' }}>
          <span style={{ fontFamily:T.sans, fontSize:14.5, fontWeight:600, color:col, minWidth:0 }}>{p.lbl(v)}</span>
          <span style={{ marginLeft:'auto', flex:'none', fontFamily:T.mono, fontSize:7.5, letterSpacing:'.1em', color:T.c500, background:T.g800, border:`1px solid ${T.g700}`, borderRadius:4, padding:'2px 5px' }}>FALTA EN TSM</span>
        </span>
        <span style={{ fontFamily:T.sans, fontSize:11.5, lineHeight:1.4, color:T.c500 }}>Mia le escribe: “{p.wa(v)}”</span>
      </button>
    );
  };
  return (
    <div onClick={onClose} style={{ position:'absolute', inset:0, background:'rgba(8,7,6,.62)', display:'flex', alignItems:'flex-end', zIndex:22 }}>
      <div onClick={e => e.stopPropagation()} style={{ width:'100%', background:T.g900, borderTop:`1px solid ${T.g700}`, borderRadius:'20px 20px 0 0', padding:'8px 16px 16px', display:'flex', flexDirection:'column', gap:12, animation:'tgin 200ms ease-out' }}>
        <div style={{ width:34, height:4, borderRadius:9, background:T.g700, alignSelf:'center' }} />
        <div>
          <div style={{ fontFamily:T.sans, fontSize:17, fontWeight:700, color:T.c100, letterSpacing:'-.3px' }}>¿Qué le aviso a {job.cli.split(' ')[0]}?</div>
          <div style={{ fontFamily:T.sans, fontSize:12.5, lineHeight:1.5, color:T.c500, marginTop:4 }}>Cada opción le manda un mensaje distinto a {job.cli.split(' ')[0]}. El texto es el que ves abajo.</div>
        </div>
        {/* flexShrink:0 — el contenedor es flex column con alto máximo, así que
            sin esto los botones se aplastan a su minHeight y el texto de dos
            líneas se desborda. Con esto scrollea, que es lo correcto. */}
        {confirmaCancel ? (
          <div style={{ display:'flex', flexDirection:'column', gap:9 }}>
            <div style={{ fontFamily:T.sans, fontSize:13, lineHeight:1.5, color:T.c300, padding:'0 2px 2px' }}>Antes de cancelar: ¿se puede pasar a otra fecha? Casi siempre el cliente prefiere reagendar.</div>
            <button onClick={() => { setConfirmaCancel(false); onPasar(); }} style={{ display:'flex', flexDirection:'column', alignItems:'flex-start', gap:4, flexShrink:0, padding:'12px 14px', background:T.vSoft, border:`1px solid ${T.vBorder}`, borderRadius:12, cursor:'pointer', textAlign:'left', width:'100%' }}>
              <span style={{ fontFamily:T.sans, fontSize:14.5, fontWeight:600, color:T.v600 }}>Reagendar en vez de cancelar</span>
              <span style={{ fontFamily:T.sans, fontSize:11.5, lineHeight:1.4, color:T.c500 }}>Elegís la fecha nueva y Mia se la propone.</span>
            </button>
            <button onClick={() => { setConfirmaCancel(false); onAccion('CANCELADO'); }} style={{ display:'flex', flexDirection:'column', alignItems:'flex-start', gap:4, flexShrink:0, padding:'12px 14px', background:T.coSoft, border:`1px solid ${T.coBorder}`, borderRadius:12, cursor:'pointer', textAlign:'left', width:'100%' }}>
              <span style={{ fontFamily:T.sans, fontSize:14.5, fontWeight:600, color:T.coral }}>Cancelar igual</span>
              <span style={{ fontFamily:T.sans, fontSize:11.5, lineHeight:1.4, color:T.c500 }}>Mia le avisa y le pregunta el motivo, así queda registrado.</span>
            </button>
            <button onClick={() => setConfirmaCancel(false)} style={{ minHeight:44, borderRadius:11, background:'transparent', border:`1px solid ${T.g700}`, color:T.c500, fontFamily:T.sans, fontSize:13, fontWeight:600, cursor:'pointer' }}>Volver</button>
          </div>
        ) : (
        <div style={{ display:'flex', flexDirection:'column', gap:8, maxHeight:284, overflowY:'auto', scrollbarWidth:'none' }}>
          {acc2.map(([est,lbl]) => { const tono = EST_TONO[est], mal = tono === 'coral';
            return (
              <button key={est} onClick={() => est === 'CANCELADO' ? setConfirmaCancel(true) : onAccion(est)} style={{ display:'flex', flexDirection:'column', alignItems:'flex-start', gap:4, flexShrink:0, padding:'12px 14px', background:T.g950, border:`1px solid ${mal?T.coBorder:tono==='green'?T.gBorder:T.g700}`, borderRadius:12, cursor:'pointer', textAlign:'left', width:'100%' }}>
                <span style={{ fontFamily:T.sans, fontSize:14.5, fontWeight:600, color:mal?T.coral:tono==='green'?T.green:T.c100 }}>{lbl}</span>
                <span style={{ fontFamily:T.sans, fontSize:11.5, lineHeight:1.4, color:T.c500 }}>Mia le escribe: “{MSG_WA[est]}”</span>
              </button>
            );
          })}
          {puedeReagendar && prop('REAGENDAR', onPasar)}
        </div>)}
        <button onClick={onClose} style={{ minHeight:48, borderRadius:12, background:'transparent', border:`1px solid ${T.g700}`, color:T.c300, fontFamily:T.sans, fontSize:14.5, fontWeight:600, cursor:'pointer' }}>Cerrar</button>
      </div>
    </div>
  );
}

/* ── pasar el turno ───────────────────────────────────────── */
const HORAS = ['09:00','10:00','11:00','12:00','14:00','15:00','16:00','17:00','18:00'];
function Pasar({ job, sel, setSel, patron }) {
  const v = VOZ[patron] || VOZ.C;
  const mot = MOTIVOS.find(x => x.k === sel.motivo) || MOTIVOS[0];
  return (
    <div style={{ display:'flex', flexDirection:'column', gap:14 }}>
      <Card pad={0} gap={0} style={{ overflow:'hidden' }}>
        <div style={{ padding:'14px 14px 10px' }}><Lbl>¿POR QUÉ LA PASÁS?</Lbl></div>
        {MOTIVOS.map((x,i) => { const on = x.k === mot.k;
          return (
            <button key={x.k} onClick={() => setSel({ ...sel, motivo:x.k })} style={{ display:'flex', alignItems:'flex-start', gap:11, width:'100%', padding:'11px 14px', borderTop:`1px solid ${T.g800}`, background:on?T.vSoft:'transparent', border:'none', cursor:'pointer', textAlign:'left' }}>
              <span style={{ width:18, height:18, flex:'none', borderRadius:99, marginTop:2, border:`1px solid ${on?T.v600:T.g700}`, background:on?T.v600:'transparent', display:'grid', placeItems:'center', color:'#fff', fontSize:10 }}>{on ? '✓' : ''}</span>
              <span style={{ flex:1, minWidth:0 }}>
                <span style={{ display:'block', fontFamily:T.sans, fontSize:14, fontWeight:on?700:500, color:on ? T.c100:T.c100 }}>{x.t}</span>
                <span style={{ display:'block', fontFamily:T.sans, fontSize:11.5, lineHeight:1.45, color:T.c500, marginTop:2 }}>{x.d}</span>
              </span>
            </button>
          );
        })}
      </Card>
      <Card gap={10}>
        <Lbl>{v.evc.toUpperCase()} AHORA</Lbl>
        <div style={{ fontFamily:T.mono, fontSize:17, color:T.c100 }}>{job.dia} · {job.hora}</div>
        <div style={{ paddingTop:10, borderTop:`1px solid ${T.g800}`, fontFamily:T.sans, fontSize:12.5, lineHeight:1.5, color:T.c500 }}>{mot.mantiene ? <>Elegí cuándo volvés. Mia le avisa a {job.cli.split(' ')[0]} la fecha nueva.</> : <>Elegí cuándo pasás {v.ev}. Mia se lo propone a {job.cli.split(' ')[0]} y espera que confirme.</>}</div>
      </Card>
      <Card gap={13}>
        <div>
          <Lbl mb={9}>NUEVO DÍA</Lbl>
          <div style={{ display:'grid', gridTemplateColumns:'repeat(3,1fr)', gap:7 }}>
            {['HOY','MAÑANA','SAB'].map(d => <Chip key={d} on={sel.dia===d} onClick={() => setSel({ ...sel, dia:d })}>{d === 'SAB' ? 'Sábado' : d === 'HOY' ? 'Hoy' : 'Mañana'}</Chip>)}
          </div>
        </div>
        <div style={{ paddingTop:13, borderTop:`1px solid ${T.g800}` }}>
          <Lbl mb={9}>NUEVA HORA</Lbl>
          <div style={{ display:'grid', gridTemplateColumns:'repeat(4,1fr)', gap:6 }}>
            {HORAS.map(h => { const on = sel.hora === h;
              return <button key={h} onClick={() => setSel({ ...sel, hora:h })} style={{ minHeight:44, borderRadius:9, fontFamily:T.mono, fontSize:12, cursor:'pointer', color:on ? T.c100:T.c300, background:on?T.vSoft:'transparent', border:`1px solid ${on?T.vBorder:T.g700}` }}>{h}</button>;
            })}
          </div>
        </div>
      </Card>
      <div style={{ fontFamily:T.sans, fontSize:12.5, lineHeight:1.55, color:T.c500, padding:'0 4px' }}>{mot.mantiene
        ? <>Mia le avisa que volvés <b style={{ color:T.c300 }}>{sel.dia === 'SAB' ? 'el sábado' : sel.dia === 'HOY' ? 'hoy' : 'mañana'} a las {sel.hora}</b>. La visita de hoy queda registrada.</>
        : <>Mia le propone <b style={{ color:T.c300 }}>{sel.dia === 'SAB' ? 'el sábado' : sel.dia === 'HOY' ? 'hoy' : 'mañana'} a las {sel.hora}</b> y espera que confirme. Si no le sirve, vuelve a preguntarte.</>}</div>
    </div>
  );
}

/* La miniatura tiene que verse en las DOS superficies: en la web las notas
   mostraban sólo el ícono de cámara y no se sabía si había foto cargada.
   Acá la foto es un recuadro con su marco y su etiqueta — el prototipo no
   tiene imágenes reales, pero el hueco es del tamaño y la forma correctos. */
function Miniatura({ n, size = 56 }) {
  const tonos = [['#2E2A44','#4A4270'], ['#2A3A32','#3E5A4A'], ['#3A2E2A','#5A4438']];
  const [a,b] = tonos[(n.tono || 0) % 3];
  return (
    <span style={{ width:size, height:size, flex:'none', borderRadius:9, border:`1px solid ${T.g700}`, overflow:'hidden', position:'relative', display:'block',
      background:`linear-gradient(135deg, ${a} 0%, ${b} 100%)` }}>
      <svg viewBox="0 0 24 24" width={size*.38} height={size*.38} fill="none" stroke="rgba(255,255,255,.5)" strokeWidth="1.4" style={{ position:'absolute', top:'50%', left:'50%', transform:'translate(-50%,-50%)' }}><path d="M3 8a2 2 0 0 1 2-2h2l1.5-2h7L17 6h2a2 2 0 0 1 2 2v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/><circle cx="12" cy="12.5" r="3.4"/></svg>
      <span style={{ position:'absolute', left:0, right:0, bottom:0, fontFamily:T.mono, fontSize:7, letterSpacing:'.08em', color:'rgba(255,255,255,.75)', background:'rgba(0,0,0,.45)', textAlign:'center', padding:'2px 0' }}>FOTO</span>
    </span>
  );
}

/* ── notas y fotos del trabajo ────────────────────────────────
   Para el que va al domicilio: lo que vio, la medida que tomó, la foto del
   tablero. Vive en el ticket, no en la cabeza del que fue.                */
function Notas({ job, onAdd, onFoto }) {
  const [txt, setTxt] = React.useState('');
  const notas = job.notas || [];
  return (
    <div style={{ display:'flex', flexDirection:'column', gap:14 }}>
      <Card gap={11}>
        <Lbl>ANOTAR ALGO</Lbl>
        <textarea value={txt} onChange={e => setTxt(e.target.value)} rows={3} placeholder="Lo que viste, una medida, un pendiente…"
          style={{ width:'100%', boxSizing:'border-box', padding:'11px 13px', background:T.g950, border:`1px solid ${txt?T.vBorder:T.g700}`, borderRadius:11, color:T.c100, fontFamily:T.sans, fontSize:14, lineHeight:1.5, outline:'none', resize:'none' }} />
        <div style={{ display:'flex', gap:8 }}>
          <button onClick={() => onFoto()} style={{ flex:1, minHeight:46, borderRadius:11, background:'transparent', border:`1px solid ${T.g700}`, color:T.c300, fontFamily:T.sans, fontSize:13.5, fontWeight:600, cursor:'pointer', display:'flex', alignItems:'center', justifyContent:'center', gap:8 }}>
            <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke={T.c300} strokeWidth="1.8"><path d="M3 8a2 2 0 0 1 2-2h2l1.5-2h7L17 6h2a2 2 0 0 1 2 2v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/><circle cx="12" cy="12.5" r="3.4"/></svg>
            Sacar foto
          </button>
          <button onClick={() => { if (txt.trim()) { onAdd(txt.trim()); setTxt(''); } }} disabled={!txt.trim()}
            style={{ flex:1, minHeight:46, borderRadius:11, background:txt.trim()?T.v600:T.g800, border:'none', color:txt.trim()?'#fff':T.c500, fontFamily:T.sans, fontSize:13.5, fontWeight:700, cursor:txt.trim()?'pointer':'default' }}>Guardar nota</button>
        </div>
      </Card>
      {notas.length > 0 && (
        <Card pad={0} gap={0} style={{ overflow:'hidden' }}>
          <div style={{ padding:'14px 14px 10px' }}><Lbl>{notas.length} {notas.length === 1 ? 'ANOTACIÓN' : 'ANOTACIONES'}</Lbl></div>
          {notas.map((n,i) => (
            <div key={i} style={{ display:'flex', gap:11, padding:'12px 14px', borderTop:`1px solid ${T.g800}` }}>
              {n.foto ? <Miniatura n={n} /> : <span style={{ width:3, flex:'none', borderRadius:9, background:T.vBorder, display:'block' }} />}
              <span style={{ flex:1, minWidth:0 }}>
                <span style={{ display:'block', fontFamily:T.sans, fontSize:13.5, lineHeight:1.5, color:T.c300 }}>{n.t}</span>
                <span style={{ display:'block', fontFamily:T.mono, fontSize:9, letterSpacing:'.09em', color:T.c500, marginTop:4 }}>{n.cuando}</span>
              </span>
            </div>
          ))}
        </Card>
      )}
      <div style={{ fontFamily:T.sans, fontSize:12.5, lineHeight:1.55, color:T.c500, padding:'0 4px' }}>Las notas son tuyas: el cliente no las ve. Quedan pegadas al trabajo, así que si volvés dentro de seis meses siguen acá.</div>
    </div>
  );
}

/* Los avisos de Mate no se inventan: están en reports.js con su copy y su
   cron —resumen de apertura, cierre de jornada a la hora_fin del emprendedor,
   reporte semanal los viernes 21hs— más notifications.js (recordatorio de
   turno a las 8am) y el bloque de presupuestos sin respuesta que el cierre
   ya inyecta. Acá se reproduce ese hilo con los datos vivos.             */
function mateHilo(jobs, cfg) {
  const hoy = jobs.filter(j => j.dia === 'HOY' && conHora(j));
  const fin = hoy.filter(j => esCobrado(j.tsm));
  const pend = hoy.filter(j => !esCierre(j.tsm));
  const canc = jobs.filter(j => j.tsm === 'CANCELADO');
  const sinResp = jobs.filter(j => !conHora(j) && j.enviado && !esCierre(j.tsm));
  const facturado = fin.reduce((s,j) => s + j.monto, 0);
  const hFin = (cfg.horario.J || [['09:00','19:00']]).slice(-1)[0][1];
  const m = [];
  m.push({ h:'08:00', tipo:'RECORDATORIOS', t:hoy.length
    ? `Les recordé el turno a ${hoy.length} ${hoy.length === 1 ? 'cliente' : 'clientes'} de hoy.`
    : 'Hoy no hay turnos, no mandé recordatorios.' });
  m.push({ h:'08:40', tipo:'APERTURA', t:hoy.length
    ? `Buen día. Arrancás con ${hoy.length} ${hoy.length === 1 ? 'trabajo' : 'trabajos'} y ${money(hoy.reduce((s,j) => s + j.monto, 0))} para cobrar.`
    : 'Buen día. Hoy no tenés nada agendado.' });
  jobs.filter(j => /Reprogramado/.test(j.extra || '')).forEach(j =>
    m.push({ h:'09:15', tipo:'CAMBIO', t:`${j.cli.split(' ')[0]} pidió pasar el turno. Quedó ${j.dia === 'HOY' ? 'hoy' : j.dia === 'MAÑANA' ? 'mañana' : 'el sábado'} ${j.hora}.` }));
  canc.forEach(j => m.push({ h:'10:02', tipo:'CANCELACIÓN', t:`Se canceló ${j.svc} de ${j.cli.split(' ')[0]}. Eran ${money(j.monto)}.` }));
  m.push({ h:hFin, tipo:'CIERRE DE JORNADA', t:`Cierre del día: ${fin.length} finalizado${fin.length === 1 ? '' : 's'}${facturado ? ' · ' + money(facturado) : ''}${pend.length ? ` · ${pend.length} sin cerrar` : ''}. ${pend.length ? 'Marcalos desde acá o el panel.' : 'Buen trabajo.'}` });
  if (sinResp.length) m.push({ h:hFin, tipo:'SIN RESPUESTA', t:`${sinResp.length} presupuesto${sinResp.length === 1 ? '' : 's'} sin contestar: ${sinResp.map(j => j.cli.split(' ')[0]).join(', ')}. ¿Le insisto?` });
  m.push({ h:'VIE 21:00', tipo:'REPORTE SEMANAL', t:'Facturación de la semana, trabajos realizados, promedio de reseñas y el progreso contra tu meta.' });
  return m;
}

function MateHilo({ jobs, cfg }) {
  const m = mateHilo(jobs, cfg);
  const col = { RECORDATORIOS:T.c500, APERTURA:T.v600, CAMBIO:T.amber, 'CANCELACIÓN':T.coral, 'CIERRE DE JORNADA':T.green, 'SIN RESPUESTA':T.amber, 'REPORTE SEMANAL':T.v600 };
  return (
    <div style={{ display:'flex', flexDirection:'column', gap:14 }}>
      <Card gap={10}>
        <Lbl>QUÉ TE AVISA MATE</Lbl>
        <div style={{ fontFamily:T.sans, fontSize:13, lineHeight:1.55, color:T.c300 }}>Recordatorios de turno a las 8, apertura del día, cambios y cancelaciones a medida que pasan, cierre de jornada a tu hora de cierre y el reporte semanal los viernes a las 21. <b style={{ color:T.c100 }}>Todo por este mismo chat</b> — el mail queda para suscripción y facturación.</div>
      </Card>
      <div style={{ display:'flex', flexDirection:'column', gap:9 }}>
        {m.map((x,i) => (
          <div key={i} style={{ display:'flex', gap:11, alignItems:'flex-start' }}>
            <span style={{ width:38, flex:'none', fontFamily:T.mono, fontSize:9.5, color:T.c500, paddingTop:11, textAlign:'right' }}>{x.h}</span>
            <div style={{ flex:1, minWidth:0, background:T.g900, border:`1px solid ${T.g800}`, borderLeft:`3px solid ${col[x.tipo] || T.g700}`, borderRadius:'12px 12px 12px 4px', padding:'11px 13px' }}>
              <div style={{ fontFamily:T.mono, fontSize:8.5, letterSpacing:'.11em', color:col[x.tipo] || T.c500, marginBottom:5 }}>{x.tipo}</div>
              <div style={{ fontFamily:T.sans, fontSize:13.5, lineHeight:1.5, color:T.c300 }}>{x.t}</div>
            </div>
          </div>
        ))}
      </div>
      <div style={{ fontFamily:T.sans, fontSize:12.5, lineHeight:1.55, color:T.c500, padding:'0 4px' }}>Los avisos y sus horarios son los de <code style={{ fontFamily:T.mono, fontSize:11 }}>reports.js</code> y <code style={{ fontFamily:T.mono, fontSize:11 }}>notifications.js</code>. El hilo sale del estado real: si cancelás o reagendás, el aviso aparece acá.</div>
    </div>
  );
}

Object.assign(window, { useArmar, saltoHabla, MOTIVOS, trasReagenda, anotar, Bitacora, metricasBit, hora, evidencia, mateHilo, MateHilo, Miniatura, TSM_ACCIONES, EST_LBL, EST_TONO, MSG_WA, VOZ, PROPUESTAS, accionesDe, esCierre, COBRADOS, esCobrado, SheetAcc, Pasar, Notas, HORAS });
