/* Bernard Health SeniorCare V2.1.4 — motor clínico global estable.
   Mantiene el diseño original. Usa Supabase como fuente principal para signos,
   incidentes, dieta, citas y cuidados. Evita caché clínica local como verdad. */
(function(){
  'use strict';
  const VERSION='3.2.1';
  const TABLES={
    vitals:'seniorcare_vital_records',
    incidents:'seniorcare_incident_records',
    diet:'seniorcare_diet_records',
    appointments:'seniorcare_appointment_records',
    care:'seniorcare_care_tasks'
  };
  const clientId=globalThis.crypto?.randomUUID?.()||`client-${Date.now()}-${Math.random().toString(16).slice(2)}`;
  let broadcastChannel=null;
  const postgresChannels=[];
  const pullTimers=new Map();

  const db=()=>window.seniorcareSupabase||null;
  const user=()=>window.currentUser||null;
  const org=()=>user()?.organizationId||user()?.organization_id||null;
  const toast=m=>window.showToast?.(m);
  const now=()=>new Date().toISOString();
  const uuid=()=>crypto?.randomUUID?.()||`${Date.now()}-${Math.random().toString(16).slice(2)}`;
  const clean=v=>String(v??'').trim();
  const asId=v=>String(v??'');
  const residentsList=()=>{ try{return Array.isArray(residents)?residents:[]}catch(_){return window.residents||[]} };
  const currentResident=()=>{ try{return selectedResident}catch(_){return window.selectedResident||residentsList()[0]} };
  const setCurrentResident=r=>{ try{ selectedResident=r; }catch(_){} window.selectedResident=r; };
  const residentKeyOf=r=>String(r?.resident_key ?? r?.id ?? r?.key ?? '').trim();
  const resById=id=>residentsList().find(r=>residentKeyOf(r)===String(id)||String(r.id)===String(id));
  const resByName=name=>residentsList().find(r=>String(r.name)===String(name));
  const renderScreen=(screen)=>{
    const keepY=window.scrollY||0;
    requestAnimationFrame(()=>{
      try{
        const s=screen||window.activeScreen;
        if(s==='vitals'&&typeof renderVitals==='function') renderVitals();
        else if(s==='incidents'&&typeof renderIncidents==='function') renderIncidents();
        else if(s==='nutrition'&&typeof renderNutrition==='function') renderNutrition();
        else if(s==='agenda'&&typeof renderAgenda==='function') renderAgenda();
        else if(s==='care'&&typeof renderCare==='function') renderCare();
        else if(s==='home'&&typeof renderHome==='function') renderHome();
        else if(s==='resident'&&typeof renderResidentDetail==='function') renderResidentDetail();
        window.bindActions?.();
      }catch(e){ console.warn('[BHSC sync] render:',e); }
      requestAnimationFrame(()=>window.scrollTo(0,keepY));
    });
  };


  async function retry(task, attempts=2){
    let last;
    for(let i=0;i<attempts;i++){
      try{return await task();}catch(e){last=e;if(i+1<attempts)await new Promise(r=>setTimeout(r,450));}
    }
    if(/Load failed|Failed to fetch|NetworkError/i.test(String(last?.message||last))) throw new Error('No se pudo conectar con Supabase. Revise internet y vuelva a intentar.');
    throw last;
  }

  const dirtyClinicalForms=new WeakSet();
  const clinicalFormIds=new Set(['vitalsForm','incidentForm','nutritionForm','appointmentForm']);
  function markClinicalFormDirty(event){
    const form=event.target?.closest?.('form');
    if(form&&clinicalFormIds.has(form.id)&&event.isTrusted)dirtyClinicalForms.add(form);
  }
  document.addEventListener('input',markClinicalFormDirty,true);
  document.addEventListener('change',markClinicalFormDirty,true);
  function clinicalFormActive(){
    const form=document.getElementById('vitalsForm')||document.getElementById('incidentForm')||document.getElementById('nutritionForm')||document.getElementById('appointmentForm');
    if(!form) return false;
    const ae=document.activeElement;
    if(ae&&form.contains(ae)) return true;
    return dirtyClinicalForms.has(form);
  }
  function snapshotClinicalForm(screen){
    const ids={vitals:'vitalsForm',incidents:'incidentForm',nutrition:'nutritionForm',agenda:'appointmentForm'};
    const form=document.getElementById(ids[screen]||'');
    if(!form) return null;
    const values={};
    for(const el of form.elements){
      if(!el.name) continue;
      if(el.type==='checkbox'||el.type==='radio') values[el.name]=el.checked;
      else values[el.name]=el.value;
    }
    return {id:form.id,values,dirty:dirtyClinicalForms.has(form),focusName:form.contains(document.activeElement)?document.activeElement?.name:null};
  }
  function restoreClinicalForm(snapshot){
    if(!snapshot) return;
    const form=document.getElementById(snapshot.id);
    if(!form) return;
    for(const [name,value] of Object.entries(snapshot.values)){
      const el=form.elements[name];
      if(!el) continue;
      if(el.type==='checkbox'||el.type==='radio') el.checked=!!value;
      else el.value=value??'';
    }
    if(snapshot.dirty) dirtyClinicalForms.add(form);
    if(snapshot.focusName) form.elements[snapshot.focusName]?.focus?.({preventScroll:true});
  }
  function renderPreservingForm(screen){
    const snapshot=snapshotClinicalForm(screen);
    renderScreen(screen);
    requestAnimationFrame(()=>requestAnimationFrame(()=>restoreClinicalForm(snapshot)));
  }
  function safeRender(screen){
    if(clinicalFormActive()) return;
    renderScreen(screen);
  }

  function exposeGlobals(){
    try{ if(typeof residents!=='undefined'&&!Object.getOwnPropertyDescriptor(window,'residents')) Object.defineProperty(window,'residents',{configurable:true,get(){return residents},set(v){residents=v}}); }catch(_){}
    try{ if(typeof selectedResident!=='undefined'&&!Object.getOwnPropertyDescriptor(window,'selectedResident')) Object.defineProperty(window,'selectedResident',{configurable:true,get(){return selectedResident},set(v){selectedResident=v}}); }catch(_){}
    try{ if(typeof residentScoped!=='undefined'&&!Object.getOwnPropertyDescriptor(window,'residentScoped')) Object.defineProperty(window,'residentScoped',{configurable:true,get(){return residentScoped},set(v){residentScoped=v}}); }catch(_){}
    try{ if(typeof vitalRecords!=='undefined'&&!Object.getOwnPropertyDescriptor(window,'vitalRecords')) Object.defineProperty(window,'vitalRecords',{configurable:true,get(){return vitalRecords},set(v){vitalRecords=v}}); }catch(_){}
    try{ if(typeof incidents!=='undefined'&&!Object.getOwnPropertyDescriptor(window,'incidents')) Object.defineProperty(window,'incidents',{configurable:true,get(){return incidents},set(v){incidents=v}}); }catch(_){}
    try{ if(typeof nutritionRecords!=='undefined'&&!Object.getOwnPropertyDescriptor(window,'nutritionRecords')) Object.defineProperty(window,'nutritionRecords',{configurable:true,get(){return nutritionRecords},set(v){nutritionRecords=v}}); }catch(_){}
    try{ if(typeof appointments!=='undefined'&&!Object.getOwnPropertyDescriptor(window,'appointments')) Object.defineProperty(window,'appointments',{configurable:true,get(){return appointments},set(v){appointments=v}}); }catch(_){}
    try{ if(typeof carePlans!=='undefined'&&!Object.getOwnPropertyDescriptor(window,'carePlans')) Object.defineProperty(window,'carePlans',{configurable:true,get(){return carePlans},set(v){carePlans=v}}); }catch(_){}
  }
  function storeVitals(){ try{return vitalRecords}catch(_){ window.vitalRecords=window.vitalRecords||{}; return window.vitalRecords; } }
  function storeIncidents(){ try{return incidents}catch(_){ window.incidents=window.incidents||[]; return window.incidents; } }
  function storeDiet(){ try{return nutritionRecords}catch(_){ window.nutritionRecords=window.nutritionRecords||{}; return window.nutritionRecords; } }
  function storeAppointments(){ try{return appointments}catch(_){ window.appointments=window.appointments||[]; return window.appointments; } }
  function storeCare(){ try{return carePlans}catch(_){ window.carePlans=window.carePlans||{}; return window.carePlans; } }
  const persist={
    vitals:()=>localStorage.setItem('seniorcare-vitals',JSON.stringify(storeVitals())),
    incidents:()=>localStorage.setItem('seniorcare-incidents',JSON.stringify(storeIncidents())),
    diet:()=>localStorage.setItem('seniorcare-nutrition',JSON.stringify(storeDiet())),
    appointments:()=>localStorage.setItem('seniorcare-appointments',JSON.stringify(storeAppointments())),
    care:()=>localStorage.setItem('seniorcare-care-plans',JSON.stringify(storeCare()))
  };

  // Firma canónica recursiva. La versión anterior usaba un replacer con solo
  // las claves del nivel raíz; por eso ignoraba cambios dentro de signos,
  // incidentes, dietas, citas y cuidados y no redibujaba la pantalla.
  function canonicalize(value,seen=new WeakSet()){
    if(value===null||typeof value!=='object') return value;
    if(seen.has(value)) return '[Circular]';
    seen.add(value);
    if(Array.isArray(value)) return value.map(item=>canonicalize(item,seen));
    const out={};
    Object.keys(value).sort().forEach(key=>{ out[key]=canonicalize(value[key],seen); });
    return out;
  }
  function stableSignature(value){
    try{return JSON.stringify(canonicalize(value));}
    catch(_){try{return JSON.stringify(value);}catch(__){return '';}}
  }
  function tableSignature(table){
    if(table===TABLES.vitals)return stableSignature(storeVitals());
    if(table===TABLES.incidents)return stableSignature(storeIncidents());
    if(table===TABLES.diet)return stableSignature(storeDiet());
    if(table===TABLES.appointments)return stableSignature(storeAppointments());
    if(table===TABLES.care)return stableSignature(storeCare());
    return '';
  }
  function currentScreenUses(table){
    const screen=window.activeScreen||'';
    const map={
      [TABLES.vitals]:['vitals','home','resident'],
      [TABLES.incidents]:['incidents','home','resident'],
      [TABLES.diet]:['nutrition','resident'],
      [TABLES.appointments]:['agenda','home','resident'],
      [TABLES.care]:['care','home','resident']
    };
    return (map[table]||[]).includes(screen);
  }
  function scheduleTablePull(table,delay=80){
    clearTimeout(pullTimers.get(table));
    pullTimers.set(table,setTimeout(()=>{
      pullTimers.delete(table);
      handleRemoteTableChange(table).catch(e=>console.warn('[BHSC live '+table+']',e));
    },delay));
  }
  async function notifyClinicalChange(table){
    try{
      if(!broadcastChannel)return;
      await broadcastChannel.send({type:'broadcast',event:'clinical-change',payload:{table,organizationId:org(),clientId,at:Date.now()}});
    }catch(e){console.warn('[BHSC broadcast send]',e);}
  }

  async function pullVitals({render=true}={}){
    if(!db()||!org())return;
    const {data,error}=await retry(()=>db().from(TABLES.vitals).select('*').eq('organization_id',org()).eq('active',true).order('recorded_at',{ascending:false}));
    if(error) throw error;
    const grouped={};
    (data||[]).forEach(row=>{
      const d=row.data||{}; const key=asId(row.resident_key);
      (grouped[key]??=[]).push({
        ...d,
        id:d.id||row.record_key,
        date:row.recorded_at||d.date||row.created_at,
        sys:d.sys||d.systolic||'', dia:d.dia||d.diastolic||'', hr:d.hr||d.heart_rate||'', temp:d.temp||d.temperature||'', spo2:d.spo2||d.oxygen_saturation||'', glu:d.glu||d.glucose||'', weight:d.weight||'', pain:d.pain||'0 - Sin dolor', obs:d.obs||d.observations||'', archived:false
      });
    });
    const s=storeVitals(); Object.keys(s).forEach(k=>delete s[k]); Object.assign(s,grouped); persist.vitals();
    if(render) safeRender('vitals');
  }
  async function saveVitalsForm(form){
    const fd=new FormData(form); const requestedId=asId(fd.get('residentId')||residentKeyOf(currentResident()));
    const recordId=asId(fd.get('recordId')||uuid()); const resident=resById(requestedId)||currentResident(); const residentId=asId(residentKeyOf(resident)||requestedId);
    const sys=clean(fd.get('sys')), dia=clean(fd.get('dia')), hr=clean(fd.get('hr')), temp=clean(fd.get('temp')), spo2=clean(fd.get('spo2'));
    if(!sys||!dia||!hr||!temp||!spo2) throw new Error('Complete presión, frecuencia, temperatura y saturación.');
    const existing=(storeVitals()[residentId]||[]).find(x=>asId(x.id)===recordId); const date=existing?.date||now();
    const payload={id:recordId,date,sys,dia,hr,temp,spo2,glu:clean(fd.get('glu')),weight:clean(fd.get('weight')),pain:clean(fd.get('pain')||'0 - Sin dolor'),obs:clean(fd.get('obs')),archived:false};
    const row={organization_id:org(),resident_key:residentId,resident_name:resident?.name||resident?.full_name||'',record_key:recordId,recorded_at:date,data:payload,active:true,updated_at:now(),updated_by:user()?.id||null};
    const {error}=await retry(()=>db().from(TABLES.vitals).upsert(row,{onConflict:'organization_id,resident_key,record_key'})); if(error) throw error;
    await pullVitals({render:false}); await notifyClinicalChange(TABLES.vitals); form.reset(); if(form.elements.residentId)form.elements.residentId.value=residentId; if(form.elements.recordId)form.elements.recordId.value=''; document.getElementById('cancelVitalEdit')?.classList.add('hidden'); renderScreen('vitals');
  }
  async function setVitalActive(recordId,residentId,active){
    let error=null;
    if(active===false){
      const res=await db().from(TABLES.vitals).delete().eq('organization_id',org()).eq('record_key',asId(recordId));
      error=res.error;
    }else{
      const res=await db().from(TABLES.vitals).update({active:true,updated_at:now(),updated_by:user()?.id||null}).eq('organization_id',org()).eq('record_key',asId(recordId));
      error=res.error;
    }
    if(error) throw error;
    await pullVitals({render:false}); await notifyClinicalChange(TABLES.vitals); renderScreen('vitals');
  }

  async function pullIncidents({render=true}={}){
    if(!db()||!org())return;
    const {data,error}=await db().from(TABLES.incidents).select('*').eq('organization_id',org()).eq('active',true).order('occurred_at',{ascending:false}); if(error) throw error;
    const list=storeIncidents(); list.splice(0,list.length,...(data||[]).map(row=>({
      ...(row.data||{}), id:row.incident_key, resident:row.resident_name||row.data?.resident||'', residentKey:row.resident_key, type:row.incident_type||row.data?.type||'Otro', description:row.description||row.data?.description||'', level:row.severity||row.data?.level||'Media', action:row.action_taken||row.data?.action||'', status:row.status||row.data?.status||'Por revisar', date:row.data?.date||new Date(row.occurred_at||row.created_at).toLocaleString('es-CR',{dateStyle:'short',timeStyle:'short'}), occurredAt:row.occurred_at||row.created_at
    }))); persist.incidents(); if(render) safeRender('incidents');
  }
  async function saveIncidentForm(form){
    const fd=new FormData(form); const residentName=clean(fd.get('resident')||currentResident()?.name); const resident=resByName(residentName)||currentResident();
    const id=clean(fd.get('incidentId'))||uuid(); const type=clean(fd.get('type')||'Otro'); const description=clean(fd.get('description'));
    if(!residentName||!description) throw new Error('Complete residente y descripción.');
    const item={id,resident:residentName,type,description,level:clean(fd.get('level')||'Media'),date:clean(fd.get('date'))||new Date().toLocaleString('es-CR',{dateStyle:'short',timeStyle:'short'}),action:clean(fd.get('action')),status:clean(fd.get('status')||'Por revisar')};
    const row={organization_id:org(),resident_key:asId(residentKeyOf(resident)||residentName),resident_name:residentName,incident_key:id,incident_type:type,severity:item.level,description,action_taken:item.action,status:item.status,occurred_at:now(),data:item,active:true,updated_at:now(),updated_by:user()?.id||null,created_by:user()?.id||null};
    const {error}=await db().from(TABLES.incidents).upsert(row,{onConflict:'organization_id,resident_key,incident_key'}); if(error) throw error; await pullIncidents({render:false}); await notifyClinicalChange(TABLES.incidents); form.reset(); if(form.elements.incidentId)form.elements.incidentId.value=''; document.getElementById('cancelIncidentEdit')?.classList.add('hidden'); renderScreen('incidents');
  }
  async function setIncidentActive(id,active){const {error}=await db().from(TABLES.incidents).update({active,updated_at:now(),updated_by:user()?.id||null}).eq('organization_id',org()).eq('incident_key',asId(id)); if(error) throw error; await pullIncidents({render:false}); await notifyClinicalChange(TABLES.incidents); renderScreen('incidents');}

  async function pullDiet({render=true}={}){
    if(!db()||!org())return;
    const {data,error}=await db().from(TABLES.diet).select('*').eq('organization_id',org()).eq('active',true).order('recorded_at',{ascending:false}); if(error) throw error;
    const grouped={}; (data||[]).forEach(row=>{const d=row.data||{}; const key=asId(row.resident_key); (grouped[key]??=[]).push({...d,id:row.record_key,date:row.recorded_at||d.date||row.created_at,meal:row.meal_type||d.meal,percent:d.percent||(`${row.intake_percentage||''}%`),liquids:d.liquids||'',supplement:d.supplement||'',obs:row.observations||d.obs||''});});
    const s=storeDiet(); Object.keys(s).forEach(k=>delete s[k]); Object.assign(s,grouped); persist.diet(); if(render) safeRender('nutrition');
  }
  async function saveDietForm(form){
    const fd=new FormData(form); const resident=currentResident(); const residentId=asId(residentKeyOf(resident)); if(!residentId) throw new Error('Seleccione un residente.');
    const id=uuid(); const percent=clean(fd.get('percent')||''); const pctNum=parseInt(percent,10);
    const item={id,date:now(),meal:clean(fd.get('meal')),percent,liquids:clean(fd.get('liquids')),supplement:clean(fd.get('supplement')),obs:clean(fd.get('obs'))};
    const row={organization_id:org(),resident_key:residentId,resident_name:resident?.name||resident?.full_name||'',record_key:id,meal_type:item.meal,diet_type:resident?.diet||'',intake_percentage:Number.isFinite(pctNum)?pctNum:null,appetite:'',observations:item.obs,recorded_at:item.date,data:item,active:true,updated_at:now(),updated_by:user()?.id||null};
    const {error}=await db().from(TABLES.diet).upsert(row,{onConflict:'organization_id,resident_key,record_key'}); if(error) throw error; await pullDiet({render:false}); await notifyClinicalChange(TABLES.diet); form.reset(); renderScreen('nutrition');
  }
  async function setDietActive(recordId,residentId,active){const {error}=await db().from(TABLES.diet).update({active,updated_at:now(),updated_by:user()?.id||null}).eq('organization_id',org()).eq('resident_key',asId(residentId)).eq('record_key',asId(recordId)); if(error) throw error; await pullDiet({render:false}); await notifyClinicalChange(TABLES.diet); renderScreen('nutrition');}

  async function pullAppointments({render=true}={}){
    if(!db()||!org())return;
    const {data,error}=await db().from(TABLES.appointments).select('*').eq('organization_id',org()).eq('active',true).order('appointment_date',{ascending:true}); if(error) throw error;
    const list=storeAppointments(); list.splice(0,list.length,...(data||[]).map(row=>{const d=row.data||{}; const dt=row.appointment_date?new Date(row.appointment_date):null; return {...d,id:row.appointment_key,date:d.date||(dt&&!isNaN(dt)?(window.BHSC_LOCAL_DATE?window.BHSC_LOCAL_DATE(dt):new Intl.DateTimeFormat('en-CA',{timeZone:'America/Costa_Rica'}).format(dt)):''),time:d.time||(dt&&!isNaN(dt)?dt.toTimeString().slice(0,5):''),resident:row.resident_name||d.resident||'',type:row.appointment_type||d.type||'',place:row.location||d.place||'',transport:row.status||d.transport||'Pendiente',companion:d.companion||'',notes:row.observations||d.notes||''};})); persist.appointments(); if(render) safeRender('agenda');
  }
  async function saveAppointmentForm(form){
    const fd=new FormData(form); const id=clean(fd.get('appointmentId'))||uuid(); const residentName=clean(fd.get('resident')||currentResident()?.name); const resident=resByName(residentName)||currentResident();
    const date=clean(fd.get('date')), time=clean(fd.get('time')); if(!residentName||!date||!time) throw new Error('Complete residente, fecha y hora.');
    const item={id,date,time,resident:residentName,type:clean(fd.get('type')),place:clean(fd.get('place')),transport:clean(fd.get('transport')),companion:clean(fd.get('companion'))||'Sin asignar',notes:clean(fd.get('notes'))};
    const row={organization_id:org(),resident_key:asId(residentKeyOf(resident)||residentName),resident_name:residentName,appointment_key:id,appointment_date:new Date(`${date}T${time}:00`).toISOString(),appointment_type:item.type,specialist:item.type,location:item.place,reason:item.type,status:item.transport,observations:item.notes,data:item,active:true,updated_at:now(),updated_by:user()?.id||null,created_by:user()?.id||null};
    const {error}=await db().from(TABLES.appointments).upsert(row,{onConflict:'organization_id,resident_key,appointment_key'}); if(error) throw error; await pullAppointments({render:false}); await notifyClinicalChange(TABLES.appointments); form.reset(); if(form.elements.appointmentId)form.elements.appointmentId.value=''; renderScreen('agenda');
  }
  async function setAppointmentActive(id,active){const {error}=await db().from(TABLES.appointments).update({active,updated_at:now(),updated_by:user()?.id||null}).eq('organization_id',org()).eq('appointment_key',asId(id)); if(error) throw error; await pullAppointments({render:false}); await notifyClinicalChange(TABLES.appointments); renderScreen('agenda');}

  function careShiftName(value){return clean(value||'Matutino').replace(/^Turno\s+/i,'')||'Matutino';}
  function careKey(residentId,shift){return `${asId(residentId)}:${careShiftName(shift)}`;}
  async function pullCare({render=true}={}){
    if(!db()||!org())return;
    const {data,error}=await db().from(TABLES.care).select('*').eq('organization_id',org()).eq('active',true).order('updated_at',{ascending:false}); if(error) throw error;
    const grouped={}; (data||[]).forEach(row=>{const shift=careShiftName(row.shift_name||row.data?.shift); const key=careKey(row.resident_key,shift); (grouped[key]??=[]).push({...(row.data||{}),id:row.data?.id||row.task_key,name:row.task_name||row.data?.name||'Tarea',shift,done:row.completed===true,completedAt:row.completed_at,completedBy:row.completed_by});});
    const s=storeCare(); Object.keys(s).forEach(k=>delete s[k]); Object.keys(grouped).forEach(k=>s[k]=grouped[k]); persist.care(); if(render) safeRender('care');
  }
  async function saveCarePlanRemote(residentId){
    const resident=resById(residentId)||currentResident(); const stableResidentId=asId(residentKeyOf(resident)||residentId); const shift=careShiftName(document.getElementById('careShiftSelect')?.value||'Matutino'); const tasks=(storeCare()[careKey(stableResidentId,shift)]||storeCare()[careKey(residentId,shift)]||[]); const timestamp=now();
    await db().from(TABLES.care).update({active:false,updated_at:timestamp,updated_by:user()?.id||null}).eq('organization_id',org()).eq('resident_key',stableResidentId).eq('shift_name',shift);
    if(tasks.length){ const rows=tasks.map(t=>({organization_id:org(),resident_key:stableResidentId,resident_name:resident?.name||resident?.full_name||'',shift_name:shift,task_key:asId(t.id||t.name),task_name:t.name||'Tarea',completed:t.done===true,completed_at:t.done?(t.completedAt||timestamp):null,completed_by:t.done?(t.completedBy||user()?.name||'Usuario'):null,data:t,active:true,updated_at:timestamp,updated_by:user()?.id||null})); const {error}=await db().from(TABLES.care).upsert(rows,{onConflict:'organization_id,resident_key,shift_name,task_key'}); if(error) throw error; }
    await pullCare({render:false}); await notifyClinicalChange(TABLES.care); renderScreen('care');
  }

  let busy=false;
  async function guarded(action,msgOk){
    if(busy)return;
    busy=true;
    const btn=document.querySelector('#vitalsForm .primary-btn,#incidentForm .primary-btn,#nutritionForm .primary-btn,#appointmentForm .primary-btn,#saveCarePlan');
    const old=btn?.textContent;
    try{
      if(btn){btn.disabled=true; btn.textContent='Guardando...';}
      await action();
      if(msgOk)toast(msgOk);
    }catch(e){
      toast(e.message||String(e));
      console.error('[BHSC '+VERSION+']',e);
      alert(e.message||String(e));
    }finally{
      if(btn){btn.disabled=false; btn.textContent=old;}
      busy=false;
    }
  }
  document.addEventListener('submit',e=>{
    const form=e.target;
    if(!(form instanceof HTMLFormElement)) return;
    if(form.id==='vitalsForm'){e.preventDefault();e.stopImmediatePropagation();guarded(()=>saveVitalsForm(form),'Signos guardados y sincronizados.');}
    if(form.id==='incidentForm'){e.preventDefault();e.stopImmediatePropagation();guarded(()=>saveIncidentForm(form),'Incidente guardado y sincronizado.');}
    if(form.id==='nutritionForm'){e.preventDefault();e.stopImmediatePropagation();guarded(()=>saveDietForm(form),'Registro de alimentación sincronizado.');}
    if(form.id==='appointmentForm'){e.preventDefault();e.stopImmediatePropagation();guarded(()=>saveAppointmentForm(form),'Cita guardada y sincronizada.');}
  },true);
  document.addEventListener('click',e=>{
    const t=e.target;
    const vDel=t.closest?.('[data-vital-delete]'), vArc=t.closest?.('[data-vital-archive]'), vRes=t.closest?.('[data-vital-restore]');
    if(vDel||vArc||vRes){e.preventDefault();e.stopImmediatePropagation(); const btn=vDel||vArc||vRes; if(vDel&&!confirm('¿Eliminar este registro?'))return; guarded(()=>setVitalActive(vRes?.dataset.vitalRestore||vDel?.dataset.vitalDelete||vArc?.dataset.vitalArchive,btn.dataset.vitalResident,!!vRes), vRes?'Registro restaurado.':vDel?'Registro eliminado.':'Registro archivado.'); return;}
    const iDel=t.closest?.('[data-incident-delete-id]'), iRev=t.closest?.('[data-incident-review-id]');
    if(iDel){e.preventDefault();e.stopImmediatePropagation(); if(!confirm('¿Eliminar este incidente?'))return; guarded(()=>setIncidentActive(iDel.dataset.incidentDeleteId,false),'Incidente eliminado.'); return;}
    if(iRev){e.preventDefault();e.stopImmediatePropagation(); const item=storeIncidents().find(x=>asId(x.id)===asId(iRev.dataset.incidentReviewId)); if(!item)return; const next=item.status==='Por revisar'?'En seguimiento':item.status==='En seguimiento'?'Resuelto':'Por revisar'; guarded(async()=>{const {error}=await db().from(TABLES.incidents).update({status:next,updated_at:now(),updated_by:user()?.id||null}).eq('organization_id',org()).eq('incident_key',asId(item.id)); if(error)throw error; await pullIncidents({render:false}); await notifyClinicalChange(TABLES.incidents); renderScreen('incidents');}, 'Estado actualizado.'); return;}
    const nDel=t.closest?.('[data-nutrition-delete]');
    if(nDel){e.preventDefault();e.stopImmediatePropagation(); const resident=currentResident(); const rec=(storeDiet()[asId(resident?.id)]||[])[Number(nDel.dataset.nutritionDelete)]; if(rec) guarded(()=>setDietActive(rec.id, resident.id, false),'Registro eliminado.'); return;}
    const aDel=t.closest?.('[data-appointment-delete]');
    if(aDel){e.preventDefault();e.stopImmediatePropagation(); if(!confirm('¿Eliminar esta cita?'))return; guarded(()=>setAppointmentActive(aDel.dataset.appointmentDelete,false),'Cita eliminada.'); return;}
    // El guardado del plan lo controla exclusivamente care-plan-role-workflow-v325.js.
    // No interceptar #saveCarePlan aquí: hacerlo antes impedía leer las tareas seleccionadas.
    const cCheck=t.closest?.('[data-care-check]'); if(cCheck){e.stopImmediatePropagation(); const rid=residentKeyOf(currentResident()); const shift=careShiftName(document.getElementById('careShiftSelect')?.value||'Matutino'); const tasks=storeCare()[careKey(rid,shift)]||[]; const task=tasks.find(x=>asId(x.id)===asId(cCheck.dataset.careCheck)); if(task){task.done=!!cCheck.checked; task.completedAt=task.done?now():null; task.completedBy=task.done?(user()?.name||'Usuario'):null; persist.care();} setTimeout(()=>guarded(()=>saveCarePlanRemote(rid), cCheck.checked?'Cuidado completado.':'Cuidado reabierto.'),0); return;}
    const cDel=t.closest?.('[data-care-delete]'); if(cDel){e.preventDefault();e.stopImmediatePropagation(); const rid=residentKeyOf(currentResident()); const shift=careShiftName(document.getElementById('careShiftSelect')?.value||'Matutino'); const s=storeCare(); s[careKey(rid,shift)]=(s[careKey(rid,shift)]||[]).filter(x=>asId(x.id)!==asId(cDel.dataset.careDelete)); persist.care(); renderScreen('care'); guarded(()=>saveCarePlanRemote(rid),'Tarea eliminada.'); return;}
  },true);

  document.addEventListener('change',e=>{
    const cCheck=e.target.closest?.('[data-care-check]');
    if(cCheck){
      e.stopImmediatePropagation();
      const rid=residentKeyOf(currentResident());
      const shift=careShiftName(document.getElementById('careShiftSelect')?.value||'Matutino');
      const tasks=storeCare()[careKey(rid,shift)]||[];
      const task=tasks.find(x=>asId(x.id)===asId(cCheck.dataset.careCheck));
      if(task){task.done=!!cCheck.checked; task.completedAt=task.done?now():null; task.completedBy=task.done?(user()?.name||'Usuario'):null; persist.care();}
      guarded(()=>saveCarePlanRemote(rid), cCheck.checked?'Cuidado completado.':'Cuidado reabierto.');
      return;
    }
  },true);

  document.addEventListener('change',e=>{
    const sel=e.target.closest?.('#nutritionResidentSelect,#careResidentSelect'); if(sel){ const r=resById(sel.value); if(r) setCurrentResident(r); setTimeout(()=>{ if(sel.id==='nutritionResidentSelect') pullDiet(); if(sel.id==='careResidentSelect') pullCare(); },0); }
  },true);

  async function pullAll({render=true,onlyChanged=false}={}){
    const before=new Map(Object.values(TABLES).map(t=>[t,tableSignature(t)]));
    await Promise.allSettled([pullVitals({render:false}),pullIncidents({render:false}),pullDiet({render:false}),pullAppointments({render:false}),pullCare({render:false})]);
    const changed=Object.values(TABLES).filter(t=>before.get(t)!==tableSignature(t));
    if(render&&(!onlyChanged||changed.some(currentScreenUses))) renderPreservingForm(window.activeScreen);
    return changed;
  }
  const pullByTable={
    [TABLES.vitals]:()=>pullVitals({render:false}),
    [TABLES.incidents]:()=>pullIncidents({render:false}),
    [TABLES.diet]:()=>pullDiet({render:false}),
    [TABLES.appointments]:()=>pullAppointments({render:false}),
    [TABLES.care]:()=>pullCare({render:false})
  };
  async function handleRemoteTableChange(table){
    const puller=pullByTable[table];
    if(!puller)return;
    const before=tableSignature(table);
    await puller();
    const changed=before!==tableSignature(table);
    if(changed&&currentScreenUses(table))renderPreservingForm(window.activeScreen);
  }
  let startedOrg=null;
  async function clearRealtime(){
    if(!db())return;
    if(broadcastChannel){try{await db().removeChannel(broadcastChannel)}catch(_){} broadcastChannel=null;}
    while(postgresChannels.length){const ch=postgresChannels.pop();try{await db().removeChannel(ch)}catch(_){}}
  }
  async function start(){
    exposeGlobals();
    const organization=org();
    if(!db()||!organization)return;
    if(startedOrg===organization&&postgresChannels.length===Object.values(TABLES).length)return;
    await clearRealtime();
    startedOrg=organization;
    await pullAll({render:true,onlyChanged:true});

    // Canal directo entre dispositivos. No depende de la publicación de cada tabla.
    broadcastChannel=db().channel(`bhsc-clinical-live-${organization}`,{config:{broadcast:{self:false}}})
      .on('broadcast',{event:'clinical-change'},({payload})=>{
        if(!payload||payload.organizationId!==organization||payload.clientId===clientId)return;
        if(Object.values(TABLES).includes(payload.table))scheduleTablePull(payload.table,20);
      })
      .subscribe(status=>console.info('[BHSC clinical broadcast]',status));

    // Cambios de Postgres quedan como respaldo, igual que en residentes.
    Object.values(TABLES).forEach(table=>{
      try{
        const channel=db().channel(`bhsc-${VERSION}-${table}-${organization}-${clientId.slice(0,8)}`)
          .on('postgres_changes',{event:'*',schema:'public',table,filter:`organization_id=eq.${organization}`},()=>scheduleTablePull(table,60))
          .subscribe(status=>console.info('[BHSC clinical postgres]',table,status));
        postgresChannels.push(channel);
      }catch(e){console.warn('Realtime',table,e);}
    });
  }
  window.BHSC_CLINICAL_SYNC_STATUS=()=>({
    version:VERSION,
    organizationId:org(),
    startedOrg,
    postgresChannels:postgresChannels.length,
    broadcastConnected:!!broadcastChannel,
    activeScreen:window.activeScreen||null,
    signatures:Object.fromEntries(Object.values(TABLES).map(t=>[t,tableSignature(t)]))
  });
  window.BHSC_FORCE_SYNC=()=>pullAll({render:true,onlyChanged:true});
  window.seniorcarePullVitals=pullVitals; window.seniorcarePullIncidents=pullIncidents; window.seniorcarePullDiet=pullDiet; window.seniorcarePullAppointments=pullAppointments; window.seniorcarePullCare=pullCare;
  window.BHSC_SAVE_CARE_PLAN=saveCarePlanRemote;
  window.BHSC_NOTIFY_CLINICAL_CHANGE=notifyClinicalChange;
  window.addEventListener('seniorcare:user-ready',start);
  window.addEventListener('load',()=>setTimeout(start,1000));
  const refreshOnReturn=()=>{if(!document.hidden)pullAll({render:true,onlyChanged:true}).catch(e=>console.warn('[BHSC return sync]',e));};
  document.addEventListener('visibilitychange',refreshOnReturn);
  window.addEventListener('pageshow',refreshOnReturn);
  window.addEventListener('online',refreshOnReturn);
})();
