// Central Dashboard — desktop. Shell routes between tabs.
// Accepts optional `tab` + `onTabChange` props for external control (e.g. eingebettete Demo).

function dashboardDayNavBtn() {
  return {
    background: CINEV.card,
    border: `1px solid ${CINEV.line}`,
    borderRadius: 6,
    padding: '6px 12px',
    fontSize: 12,
    color: CINEV.inkSoft,
    cursor: 'pointer',
    fontFamily: 'inherit',
  };
}

/** Nur auf dem Tab „Heute in der Praxis“ (today) — nicht auf Woche/Patienten/… */
function DashboardLangSwitch() {
  const lang = useLang();
  return (
    <div style={{
      position: 'fixed', top: 12, right: 12, zIndex: 1000, display: 'flex', background: CINEV.card,
      border: `1px solid ${CINEV.line}`, borderRadius: 999, padding: 4,
      boxShadow: '0 4px 16px rgba(0,0,0,0.08)',
      fontFamily: '"Inter", -apple-system, sans-serif',
    }}>
      {['de', 'es'].map((l) => (
        <button key={l} type="button" onClick={() => window.setLang(l)} style={{
          border: 'none', cursor: 'pointer', fontFamily: 'inherit', padding: '6px 14px', borderRadius: 999,
          background: lang === l ? CINEV.teal : 'transparent', color: lang === l ? '#fff' : CINEV.inkSoft,
          fontSize: 12, fontWeight: 600, letterSpacing: 0.5, textTransform: 'uppercase',
        }}>{l}</button>
      ))}
    </div>
  );
}

function Dashboard({ tab: controlledTab, onTabChange } = {}) {
  useLang();
  const [internalTab, setInternalTab] = React.useState('today');
  const tab = controlledTab ?? internalTab;
  const [initialPatientFromWeek, setInitialPatientFromWeek] = React.useState(null);
  /** Erhöht bei jedem Klick auf „Patienten“ in der Sidebar → Patientenübersicht, auch aus PatientDetail. */
  const [patientsNavNonce, setPatientsNavNonce] = React.useState(0);
  const [therapistHighlightId, setTherapistHighlightId] = React.useState(null);
  const [therapistCreateNonce, setTherapistCreateNonce] = React.useState(0);
  const [weekAnchorYmd, setWeekAnchorYmd] = React.useState(() => cinevYmdWallToday());
  const [dayAnchorYmd, setDayAnchorYmd] = React.useState(() => cinevYmdWallToday());
  const setTab = React.useCallback((k) => {
    if (onTabChange) onTabChange(k);
    else setInternalTab(k);
    if (k !== 'patients') setInitialPatientFromWeek(null);
    if (k !== 'therapists') setTherapistHighlightId(null);
  }, [onTabChange]);

  const TAB_META = {
    today:      { titleKey: 'day_title' },
    week:       { titleKey: 'wk_title' },
    patients:   { titleKey: 'pt_title' },
    therapists: { titleKey: 'tp_title' },
    stats:      { titleKey: 'st_title' },
    admindb:    { titleKey: 'ad_title' },
  };
  const meta = TAB_META[tab] || TAB_META.today;

  const topbarActions = {
    today:      [],
    week:       [],
    patients:   [],
    therapists: [['primary', '＋', 'tp_add_therapist', () => setTherapistCreateNonce((n) => n + 1)]],
    stats:      [['ghost', '⇩', 'st_export']],
    admindb:    [],
  };

  return (
    <React.Fragment>
      {tab === 'today' ? <DashboardLangSwitch /> : null}
      <div style={{
      width: '100%', height: '100%', background: CINEV.bg, color: CINEV.ink,
      fontFamily: '"Inter", -apple-system, sans-serif',
      display: 'grid', gridTemplateColumns: '220px 1fr', fontSize: 13,
    }}>
      <aside style={{ background: CINEV.card, borderRight: `1px solid ${CINEV.line}`, padding: '20px 0', display: 'flex', flexDirection: 'column' }}>
        <div style={{ padding: '0 20px 24px', display: 'flex', alignItems: 'center', gap: 10 }}>
          <CinevMark size={36}/>
          <div>
            <div style={{ fontSize: 15, fontWeight: 700, color: CINEV.teal, letterSpacing: -0.2 }}>CINEV</div>
            <div style={{ fontSize: 9.5, color: CINEV.inkMute, letterSpacing: 0.8, textTransform: 'uppercase' }}>{window.__cinevLang === 'es' ? 'Gestión' : 'Praxismanagement'}</div>
          </div>
        </div>
        <nav style={{ padding: '0 12px', display: 'flex', flexDirection: 'column', gap: 2 }}>
          {[['today', t('nav_today'), '📅'], ['week', t('nav_week'), '◨'], ['patients', t('nav_patients'), '◔'],
            ['therapists', t('nav_therapists'), '◉'], ['stats', t('nav_stats'), '◫'],
            ['admindb', t('nav_admindb'), '▤']].map(([k, label, ico]) => (
            <button key={k} onClick={() => {
              if (k === 'patients') setPatientsNavNonce((n) => n + 1);
              setTab(k);
            }} style={{
              display: 'flex', alignItems: 'center', gap: 10, padding: '8px 12px', border: 'none',
              borderRadius: 6, cursor: 'pointer', textAlign: 'left', fontFamily: 'inherit', fontSize: 13,
              background: tab === k ? CINEV.soft : 'transparent',
              color: tab === k ? CINEV.teal : CINEV.inkSoft, fontWeight: tab === k ? 600 : 400,
            }}>
              <span style={{ width: 16, textAlign: 'center', opacity: 0.7 }}>{ico}</span>{label}
            </button>
          ))}
        </nav>
        <div style={{ marginTop: 'auto', padding: 16, borderTop: `1px solid ${CINEV.line}` }}>
          <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
            <PatientAvatar name="Regina Klatt" patientKey="sidebar-coord" size={32} />
            <div style={{ fontSize: 12 }}>
              <div style={{ fontWeight: 600 }}>Regina Klatt</div>
              <div style={{ color: CINEV.inkMute, fontSize: 11 }}>{t('role_coord')}</div>
            </div>
          </div>
        </div>
      </aside>

      <main style={{ overflow: 'hidden', display: 'flex', flexDirection: 'column' }}>
        <div style={{ padding: '20px 32px', borderBottom: `1px solid ${CINEV.line}`,
          display: 'flex', alignItems: 'center', justifyContent: 'space-between', background: CINEV.card }}>
          <div>
            <div style={{ fontSize: 22, fontWeight: 600, letterSpacing: -0.5 }}>{t(meta.titleKey)}</div>
          </div>
          <div style={{ display: 'flex', gap: 8 }}>
            {(topbarActions[tab] || []).map((entry, i) => {
              const [variant, icon, key, onAct] = entry;
              return (
                <button key={i} type="button" style={btn(variant)} onClick={typeof onAct === 'function' ? onAct : undefined}>
                  <span style={{ opacity: variant === 'primary' ? 1 : 0.5, marginRight: 6 }}>{icon}</span>{t(key)}
                </button>
              );
            })}
          </div>
        </div>

        <div style={{
          overflow: tab === 'today' ? 'hidden' : 'auto',
          flex: 1,
          minHeight: 0,
          display: 'flex',
          flexDirection: 'column',
        }}>
          {tab === 'today'      && (
            <TodayView
              anchorYmd={dayAnchorYmd}
              onAnchorYmdChange={setDayAnchorYmd}
              onOpenPatientDetail={(patientId) => {
                setInitialPatientFromWeek(String(patientId));
                setTab('patients');
              }}
            />
          )}
          {tab === 'week'       && (
            <WeekView
              anchorYmd={weekAnchorYmd}
              onAnchorYmdChange={setWeekAnchorYmd}
              onOpenPatient={(patientId) => {
                setInitialPatientFromWeek(String(patientId));
                setTab('patients');
              }}
              onOpenTherapist={(therapistId) => {
                setTherapistHighlightId(String(therapistId));
                setTab('therapists');
              }}
            />
          )}
          {tab === 'patients'   && (
            <PatientsView
              initialPatientId={initialPatientFromWeek}
              onInitialPatientConsumed={() => setInitialPatientFromWeek(null)}
              navRevealNonce={patientsNavNonce}
            />
          )}
          {tab === 'therapists' && (
            <TherapistsView
              highlightTherapistId={therapistHighlightId}
              openCreateTherapistNonce={therapistCreateNonce}
              onTherapistCreated={(id) => setTherapistHighlightId(id)}
            />
          )}
          {tab === 'stats'      && <StatsView/>}
          {tab === 'admindb'    && <AdminDbView/>}
        </div>
      </main>
    </div>
    </React.Fragment>
  );
}

function dashboardDisciplineTyKey(discipline) {
  const m = {
    behavioral: 'ty_behavior',
    speech: 'ty_speech',
    ergo: 'ty_occupational',
    neuro: 'ty_eeg',
    psychomotor: 'ty_psychomotor',
  };
  return m[discipline] || 'ty_behavior';
}

function dashboardTherapistRoleKey(discipline) {
  const m = {
    neuro: 'rl_neuro_m',
    behavioral: 'rl_behav',
    speech: 'rl_logo',
    ergo: 'rl_occ_f',
    psychomotor: 'rl_psymot',
  };
  return m[discipline] || 'rl_childpsy';
}

function dashboardApptMatrixStatus(a) {
  if (a.status === 'done') return 'done';
  if (a.status === 'no_show') return 'no_show';
  if (a.status === 'cancelled') return 'cancelled';
  const now = Date.now();
  const s = new Date(a.start_at).getTime();
  const e = new Date(a.end_at).getTime();
  if (Number.isFinite(s) && Number.isFinite(e) && s <= now && now < e) return 'now';
  return 'scheduled';
}

function dashboardTherapistMatrixOrder(appts) {
  const seen = new Set();
  const order = [];
  const sorted = [...(appts || [])]
    .filter((a) => a.status !== 'cancelled')
    .sort((x, y) => new Date(x.start_at) - new Date(y.start_at));
  for (const a of sorted) {
    const id = a.therapist_id != null ? String(a.therapist_id) : '';
    if (id && !seen.has(id)) {
      seen.add(id);
      order.push(id);
    }
  }
  return order;
}

function dashboardPatientMatrixOrder(appts) {
  const seen = new Set();
  const order = [];
  const sorted = [...(appts || [])]
    .filter((a) => a.status !== 'cancelled')
    .sort((x, y) => new Date(x.start_at) - new Date(y.start_at));
  for (const a of sorted) {
    const id = a.patient_id != null ? String(a.patient_id) : '';
    if (id && !seen.has(id)) {
      seen.add(id);
      order.push(id);
    }
  }
  return order;
}

function todayReminderTemplateLabel(key) {
  const k = String(key || '');
  if (k === 'reminder_48h') return t('rm_tpl_reminder_48h');
  if (k === 'reminder_day') return t('rm_tpl_reminder_day');
  return k || '—';
}

function todayFormatReminderSent(iso) {
  if (!iso) return t('rm_planned_send');
  const d = new Date(iso);
  const hh = String(d.getHours()).padStart(2, '0');
  const mm = String(d.getMinutes()).padStart(2, '0');
  return `${t('rm_sent_at')} ${hh}:${mm}`;
}

function TodayView({ onOpenPatientDetail, anchorYmd: anchorControlled, onAnchorYmdChange } = {}) {
  useLang();
  const live = typeof cinevLiveApi === 'function' && cinevLiveApi();
  const [internalAnchorYmd, setInternalAnchorYmd] = React.useState(() => cinevYmdWallToday());
  const anchorControlledMode = anchorControlled != null && typeof onAnchorYmdChange === 'function';
  const anchorYmd = anchorControlledMode ? anchorControlled : internalAnchorYmd;
  const setAnchorYmd = anchorControlledMode ? onAnchorYmdChange : setInternalAnchorYmd;
  const [scheduleRowAxis, setScheduleRowAxis] = React.useState('patient');
  const [liveState, setLiveState] = React.useState({
    loading: !!live,
    err: null,
    appts: [],
    therapistItems: [],
    tMap: null,
    therapistOrder: [],
    patientOrder: [],
    patientNameById: {},
    countsByTherapist: {},
    reminders: [],
  });

  React.useEffect(() => {
    if (!live) return undefined;
    let cancelled = false;
    (async () => {
      setLiveState((s) => ({ ...s, loading: true, err: null }));
      try {
        const ymd = anchorYmd;
        const [tData, apptData] = await Promise.all([
          cinevApiGet('/therapists'),
          cinevFetchAppointmentsOnDate(ymd, { limit: 400 }),
        ]);
        if (cancelled) return;
        const items = apptData.items || [];
        const tMap = cinevTherapistMapFromItems(tData.items || []);
        const therapistOrder = dashboardTherapistMatrixOrder(items);
        const patientOrder = dashboardPatientMatrixOrder(items);
        const patientNameById = {};
        for (const a of items) {
          if (a.patient_id == null) continue;
          const pid = String(a.patient_id);
          if (!patientNameById[pid]) patientNameById[pid] = (a.patient_name || '').trim() || '—';
        }
        const countsByTherapist = {};
        for (const a of items) {
          if (a.status === 'cancelled') continue;
          const tid = String(a.therapist_id);
          countsByTherapist[tid] = (countsByTherapist[tid] || 0) + 1;
        }
        let reminders = [];
        try {
          const rData = await cinevApiGet('/reminders/log?limit=200');
          reminders = (rData.items || []).filter(
            (r) => r.appointment_start && cinevYmdWallFromIso(r.appointment_start) === ymd,
          );
        } catch {
          reminders = [];
        }
        setLiveState({
          loading: false,
          err: null,
          appts: items,
          therapistItems: tData.items || [],
          tMap: Object.keys(tMap).length ? tMap : null,
          therapistOrder,
          patientOrder,
          patientNameById,
          countsByTherapist,
          reminders,
        });
      } catch (e) {
        if (!cancelled) {
          setLiveState((s) => ({
            ...s,
            loading: false,
            err: e.message || String(e),
          }));
        }
      }
    })();
    return () => { cancelled = true; };
  }, [live, anchorYmd]);

  const sidebarTherapistIds = live
    ? (() => {
      const base = liveState.therapistOrder.length
        ? [...liveState.therapistOrder]
        : (liveState.therapistItems || []).map((r) => String(r.id));
      const withToday = base.filter((id) => (liveState.countsByTherapist[id] || 0) > 0);
      return withToday.length ? withToday : base;
    })()
    : [];

  const todayTileBase = {
    background: CINEV.card,
    borderRadius: 10,
    border: `1px solid ${CINEV.line}`,
    padding: '14px 16px',
    flex: '1 1 0',
    minHeight: 0,
    display: 'flex',
    flexDirection: 'column',
    overflow: 'hidden',
  };

  return (
    <div style={{
      padding: '20px 32px',
      boxSizing: 'border-box',
      flex: 1,
      minHeight: 0,
      width: '100%',
      display: 'grid',
      gridTemplateColumns: '1fr 340px',
      gridTemplateRows: 'auto minmax(0, 1fr)',
      columnGap: 24,
      rowGap: 20,
      alignItems: 'stretch',
    }}>
      <div style={{ gridColumn: '1 / -1', display: 'flex', flexDirection: 'column', gap: 12, flexShrink: 0, minWidth: 0 }}>
        {!live && (
          <div style={{ padding: '10px 12px', borderRadius: 8, background: CINEV.soft, color: CINEV.inkSoft, fontSize: 12.5, flexShrink: 0, border: `1px solid ${CINEV.line}` }}>
            {t('ui_proto_no_live')}
          </div>
        )}
        {live && liveState.err && (
          <div style={{ padding: '10px 12px', borderRadius: 8, background: CINEV.noShow.bg, color: CINEV.noShow.fg, fontSize: 12.5, flexShrink: 0 }}>
            {liveState.err}
          </div>
        )}
        {live && liveState.loading && (
          <div style={{ fontSize: 12.5, color: CINEV.inkMute, flexShrink: 0 }}>…</div>
        )}

        <div style={{ display: 'flex', alignItems: 'center', gap: 12, flexWrap: 'wrap', flexShrink: 0 }}>
          <div style={{ display: 'flex', background: CINEV.bg, borderRadius: 6, padding: 2, border: `1px solid ${CINEV.lineSoft}` }}>
            <button
              type="button"
              onClick={() => setScheduleRowAxis('patient')}
              style={{
                border: 'none', padding: '6px 12px', borderRadius: 4, fontSize: 11.5, fontWeight: scheduleRowAxis === 'patient' ? 600 : 400,
                cursor: 'pointer', fontFamily: 'inherit',
                background: scheduleRowAxis === 'patient' ? CINEV.teal : 'transparent',
                color: scheduleRowAxis === 'patient' ? '#fff' : CINEV.inkSoft,
              }}
            >{t('wk_focus_patients')}</button>
            <button
              type="button"
              onClick={() => setScheduleRowAxis('therapist')}
              style={{
                border: 'none', padding: '6px 12px', borderRadius: 4, fontSize: 11.5, fontWeight: scheduleRowAxis === 'therapist' ? 600 : 400,
                cursor: 'pointer', fontFamily: 'inherit',
                background: scheduleRowAxis === 'therapist' ? CINEV.teal : 'transparent',
                color: scheduleRowAxis === 'therapist' ? '#fff' : CINEV.inkSoft,
              }}
            >{t('wk_focus_therapists')}</button>
          </div>
          <button type="button" style={dashboardDayNavBtn()} onClick={() => setAnchorYmd((y) => cinevYmdAddDays(y, -1))}>{t('day_prev')}</button>
          <button
            type="button"
            style={{ ...dashboardDayNavBtn(), background: CINEV.teal, color: '#fff', borderColor: CINEV.teal }}
            onClick={() => setAnchorYmd(cinevYmdWallToday())}
          >{t('wk_today')}</button>
          <button type="button" style={dashboardDayNavBtn()} onClick={() => setAnchorYmd((y) => cinevYmdAddDays(y, 1))}>{t('day_next')}</button>
        </div>
      </div>

      <div style={{ gridColumn: 1, gridRow: 2, display: 'flex', flexDirection: 'column', minHeight: 0, minWidth: 0 }}>
        <div style={{
          background: CINEV.card, borderRadius: 10, border: `1px solid ${CINEV.line}`, overflow: 'hidden',
          flex: 1, minHeight: 0, display: 'flex', flexDirection: 'column',
        }}>
          <div style={{
            padding: '14px 18px', borderBottom: `1px solid ${CINEV.line}`,
            display: 'flex', alignItems: 'center', justifyContent: 'flex-end', flexWrap: 'wrap', gap: 10,
            flexShrink: 0,
          }}>
            <div style={{ display: 'flex', gap: 14, fontSize: 11, color: CINEV.inkMute, flexWrap: 'wrap' }}>
              <Legend color={CINEV.greenDark} label={t('legend_done')}/>
              <Legend color={CINEV.teal} label={t('legend_now')}/>
              <Legend color={'#6b4d8a'} label={t('legend_special')}/>
              <Legend color={'#d9a42e'} label={t('legend_makeup')}/>
            </div>
          </div>
          <div style={{ flex: 1, minHeight: 0, overflow: 'hidden', display: 'flex', flexDirection: 'column' }}>
            <ScheduleMatrix
              rowAxis={scheduleRowAxis}
              onOpenPatientDetail={onOpenPatientDetail}
              liveAppts={live && !liveState.loading && liveState.tMap ? liveState.appts : null}
              therapistApiMap={live && !liveState.loading && liveState.tMap ? liveState.tMap : null}
              therapistIdsOrdered={live && !liveState.loading && liveState.tMap ? liveState.therapistOrder : null}
              patientIdsOrdered={live && !liveState.loading && liveState.tMap ? liveState.patientOrder : null}
              patientNameById={live && !liveState.loading && liveState.tMap ? liveState.patientNameById : null}
            />
          </div>
        </div>
      </div>

      <div style={{
        gridColumn: 2,
        gridRow: 2,
        display: 'flex',
        flexDirection: 'column',
        gap: 12,
        minHeight: 0,
        minWidth: 0,
      }}>
        <div style={todayTileBase}>
          <div style={{ fontSize: 14, fontWeight: 600, marginBottom: 14, flexShrink: 0 }}>{t('therapists_today')}</div>
          <div style={{
            display: 'flex', flexDirection: 'column', gap: 10,
            flex: 1, minHeight: 0, overflowY: 'auto', WebkitOverflowScrolling: 'touch',
          }}>
            {sidebarTherapistIds.map((tid) => {
              const ent = liveState.tMap[tid];
              const load = liveState.countsByTherapist[tid] || 0;
              const now = Date.now();
              const busy = liveState.appts.some((a) => String(a.therapist_id) === tid
                && new Date(a.start_at).getTime() <= now && now < new Date(a.end_at).getTime());
              const name = ent && ent.name ? ent.name : '—';
              const disc = ent && ent.discipline;
              return (
                <div key={tid} style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
                  <Avatar therapist={tid} size={32} therapistApiMap={liveState.tMap}/>
                  <div style={{ flex: 1, minWidth: 0 }}>
                    <div style={{ fontSize: 12.5, fontWeight: 500, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>{name}</div>
                    <div style={{ fontSize: 10.5, color: CINEV.inkMute }}>{disc ? t(dashboardTherapistRoleKey(disc)) : '—'}</div>
                  </div>
                  <div style={{ textAlign: 'right' }}>
                    <div style={{ fontSize: 11, color: CINEV.inkMute }}>{t('today_count')}</div>
                    <div style={{ fontSize: 12, fontWeight: 600, fontVariantNumeric: 'tabular-nums', color: CINEV.teal }}>{load}</div>
                  </div>
                  <div style={{ width: 7, height: 7, borderRadius: '50%', background: busy ? CINEV.green : CINEV.inkMute }}/>
                </div>
              );
            })}
          </div>
        </div>

        <div style={todayTileBase}>
          <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 8, flexWrap: 'wrap', gap: 8, flexShrink: 0 }}>
            <div style={{ fontSize: 14, fontWeight: 600 }}>{t('reminders_title')}</div>
            <div style={{ fontSize: 10, padding: '3px 7px', borderRadius: 10, background: CINEV.soft, color: CINEV.teal, fontWeight: 600 }}>{t('reminders_badge')}</div>
          </div>
          <div style={{ fontSize: 10.5, color: CINEV.inkMute, marginBottom: 10, flexShrink: 0 }}>{t('rm_today_appts_only')}</div>
          <div style={{
            display: 'flex', flexDirection: 'column', gap: 10,
            flex: 1, minHeight: 0, overflowY: 'auto', WebkitOverflowScrolling: 'touch',
          }}>
            {live && !liveState.loading
              ? (liveState.reminders.length === 0 ? (
                <div style={{ fontSize: 12, color: CINEV.inkMute }}>—</div>
              ) : (
                liveState.reminders.map((r, i) => (
                  <div key={r.id || i} style={{ display: 'flex', gap: 10, alignItems: 'flex-start' }}>
                    <div style={{ marginTop: 4, width: 6, height: 6, borderRadius: '50%', flexShrink: 0,
                      background: r.send_status === 'sent' ? CINEV.green : r.send_status === 'queued' ? CINEV.warn : CINEV.teal }}/>
                    <div style={{ flex: 1, minWidth: 0 }}>
                      <div style={{ fontSize: 12, fontWeight: 500 }}>{r.patient_name || '—'}</div>
                      <div style={{ fontSize: 11, color: CINEV.inkSoft, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>
                        {todayReminderTemplateLabel(r.template_key)}
                      </div>
                      <div style={{ fontSize: 10.5, color: CINEV.inkMute, marginTop: 2, wordBreak: 'break-all' }}>
                        {r.recipient_email || '—'}
                      </div>
                      <div style={{ fontSize: 10.5, color: CINEV.inkMute, marginTop: 1, fontVariantNumeric: 'tabular-nums' }}>
                        {todayFormatReminderSent(r.sent_at)}
                      </div>
                    </div>
                  </div>
                ))
              ))
              : (
                <div style={{ fontSize: 12, color: CINEV.inkMute }}>—</div>
              )}
          </div>
        </div>
      </div>
    </div>
  );
}

const SCHEDULE_MIN_ROWS = 10;
const MATRIX_ROW_MIN_PX = 44;
/** Gleiche Verzögerung wie Wochenansicht — Tooltips erst nach kurzer Ruhe. */
const CINEV_APPOINTMENT_TOOLTIP_DELAY_MS = 500;
const MATRIX_DAY_START_HOUR = 7;
const MATRIX_DAY_END_HOUR = 22;
const MATRIX_COL_W = 80;
const MATRIX_NAME_COL_W = 136;
const SCHEDULE_MATRIX_HOURS = (() => {
  const a = [];
  for (let h = MATRIX_DAY_START_HOUR; h <= MATRIX_DAY_END_HOUR; h += 1) {
    a.push(String(h).padStart(2, '0'));
  }
  return a;
})();

function dashboardLiveApptTooltipLines(a, therapistApiMap) {
  const si = a.session_index != null ? Number(a.session_index) + 1 : '?';
  const tid = String(a.therapist_id);
  const full = (therapistApiMap[tid] && therapistApiMap[tid].name)
    ? therapistApiMap[tid].name
    : (a.therapist_name || '').trim() || '—';
  const endIso = a.end_at || a.start_at;
  const sh = typeof cinevWallHourMinuteFromIso === 'function' ? cinevWallHourMinuteFromIso(a.start_at) : { h: new Date(a.start_at).getHours(), m: new Date(a.start_at).getMinutes() };
  const eh = typeof cinevWallHourMinuteFromIso === 'function' ? cinevWallHourMinuteFromIso(endIso) : { h: new Date(endIso).getHours(), m: new Date(endIso).getMinutes() };
  const pad = (x) => String(x).padStart(2, '0');
  const hm = (o) => (Number.isFinite(o.h) ? `${pad(o.h)}:${pad(o.m)}` : '—');
  return [
    a.patient_name || '—',
    full,
    `${hm(sh)}–${hm(eh)}`,
    `${si}/12`,
  ].join('\n');
}

function padScheduleMatrixRows(rows, axisKey) {
  const out = rows.map((r) => ({ ...r, empty: false }));
  let padIdx = 0;
  while (out.length < SCHEDULE_MIN_ROWS) {
    out.push({
      id: `__pad_${axisKey}_${padIdx++}`,
      empty: true,
      label: '',
      name: ' ',
    });
  }
  return out;
}

function ScheduleMatrix({
  rowAxis = 'patient',
  onOpenPatientDetail,
  liveAppts,
  therapistApiMap,
  therapistIdsOrdered,
  patientIdsOrdered,
  patientNameById,
}) {
  useLang();
  const portRef = React.useRef(null);
  const [matrixTip, setMatrixTip] = React.useState(null);
  const matrixTipTimerRef = React.useRef(null);
  const matrixTipMoveTsRef = React.useRef(0);
  const [viewportFillerRows, setViewportFillerRows] = React.useState(0);

  React.useEffect(() => () => {
    if (matrixTipTimerRef.current) clearTimeout(matrixTipTimerRef.current);
  }, []);
  const hours = SCHEDULE_MATRIX_HOURS;
  const timelineInnerW = hours.length * MATRIX_COL_W;
  const innerMinW = MATRIX_NAME_COL_W + timelineInnerW;
  const live = Array.isArray(liveAppts) && therapistApiMap != null;
  const usePatientAxis = rowAxis === 'patient';

  const therapistSubset = live
    ? (therapistIdsOrdered && therapistIdsOrdered.length
      ? therapistIdsOrdered
      : Object.keys(therapistApiMap)).map((id) => ({
      id,
      name: therapistApiMap[id] && therapistApiMap[id].name ? therapistApiMap[id].name : id.slice(0, 8),
    }))
    : [];

  const patientSubset = live && usePatientAxis
    ? (patientIdsOrdered && patientIdsOrdered.length
      ? patientIdsOrdered.map((id) => ({
        id,
        label: (patientNameById && patientNameById[id]) ? patientNameById[id] : id.slice(0, 8),
      }))
      : [])
    : [];

  const matrixRows = padScheduleMatrixRows(
    usePatientAxis ? patientSubset : therapistSubset,
    usePatientAxis ? 'p' : 't',
  );

  const axisFillKey = usePatientAxis ? 'p' : 't';
  React.useLayoutEffect(() => {
    const port = portRef.current;
    if (!port) return undefined;
    const measure = () => {
      const h = port.clientHeight;
      if (h <= 0) return;
      const inner = port.firstElementChild;
      const headerEl = inner?.querySelector?.('[data-schedule-matrix-header]');
      const headerH = headerEl ? headerEl.getBoundingClientRect().height : 32;
      const rowSlots = Math.max(
        SCHEDULE_MIN_ROWS,
        Math.ceil(Math.max(0, h - headerH) / MATRIX_ROW_MIN_PX),
      );
      setViewportFillerRows(Math.max(0, rowSlots - matrixRows.length));
    };
    measure();
    if (typeof ResizeObserver === 'undefined') return undefined;
    const ro = new ResizeObserver(() => measure());
    ro.observe(port);
    return () => ro.disconnect();
  }, [matrixRows.length, rowAxis]);

  React.useLayoutEffect(() => {
    const el = portRef.current;
    if (!el) return undefined;
    const snapScrollToCurrentHour = () => {
      const ch = new Date().getHours();
      if (ch < MATRIX_DAY_START_HOUR || ch > MATRIX_DAY_END_HOUR) {
        el.scrollLeft = 0;
        return;
      }
      const slot = ch - MATRIX_DAY_START_HOUR;
      const target = slot * MATRIX_COL_W;
      const maxLeft = Math.max(0, el.scrollWidth - el.clientWidth);
      el.scrollLeft = Math.max(0, Math.min(target, maxLeft));
    };
    snapScrollToCurrentHour();
    const id = requestAnimationFrame(snapScrollToCurrentHour);
    return () => cancelAnimationFrame(id);
  }, [rowAxis, matrixRows.length]);

  const fillerRows = Array.from({ length: viewportFillerRows }, (_, i) => ({
    id: `__vf_${axisFillKey}_${i}`,
    empty: true,
    label: '',
    name: ' ',
  }));
  const allMatrixRows = matrixRows.concat(fillerRows);

  const chLive = (() => {
    if (typeof cinevWallHourMinuteFromIso === 'function') {
      const { h } = cinevWallHourMinuteFromIso(new Date().toISOString());
      return Number.isFinite(h) ? h : new Date().getHours();
    }
    return new Date().getHours();
  })();
  const highlightHour = (chLive >= MATRIX_DAY_START_HOUR && chLive <= MATRIX_DAY_END_HOUR) ? chLive : null;

  const hideMatrixTip = React.useCallback(() => {
    if (matrixTipTimerRef.current) {
      clearTimeout(matrixTipTimerRef.current);
      matrixTipTimerRef.current = null;
    }
    setMatrixTip(null);
  }, []);

  const matrixTipHandlers = React.useCallback((text) => ({
    onPointerEnter: (e) => {
      if (!text) return;
      if (matrixTipTimerRef.current) clearTimeout(matrixTipTimerRef.current);
      matrixTipTimerRef.current = setTimeout(() => {
        matrixTipTimerRef.current = null;
        setMatrixTip({ x: e.clientX + 12, y: e.clientY + 12, text });
      }, CINEV_APPOINTMENT_TOOLTIP_DELAY_MS);
    },
    onPointerMove: (e) => {
      const now = Date.now();
      if (now - matrixTipMoveTsRef.current < 50) return;
      matrixTipMoveTsRef.current = now;
      setMatrixTip((prev) => (prev ? { ...prev, x: e.clientX + 12, y: e.clientY + 12 } : prev));
    },
    onPointerLeave: hideMatrixTip,
  }), [hideMatrixTip]);

  const liveTypeShort = (a) => {
    const tid = String(a.therapist_id);
    const disc = therapistApiMap[tid] && therapistApiMap[tid].discipline;
    return disc ? t(dashboardDisciplineTyKey(disc)) : '—';
  };

  const liveTherapistShort = (a) => {
    const tid = String(a.therapist_id);
    const full = (therapistApiMap[tid] && therapistApiMap[tid].name)
      ? therapistApiMap[tid].name
      : (a.therapist_name || '');
    const parts = String(full).trim().split(/\s+/);
    return parts.length ? parts[parts.length - 1] : '—';
  };

  const liveApptStyle = (a) => {
    const st = dashboardApptMatrixStatus(a);
    let color; let bg; let fg;
    if (a.status === 'no_show') {
      color = CINEV.warn; bg = '#fdf6e3'; fg = CINEV.ink;
    } else if (st === 'done') {
      color = CINEV.greenDark; bg = '#eaf3df'; fg = CINEV.ink;
    } else if (st === 'now') {
      color = CINEV.teal; bg = CINEV.teal; fg = '#fff';
    } else {
      color = CINEV.tealLight; bg = CINEV.card; fg = CINEV.ink;
    }
    return { color, bg, fg, st };
  };

  const colLabel = usePatientAxis ? t('patient_col') : t('therapist_col');

  const nameColSticky = {
    position: 'sticky',
    left: 0,
    width: MATRIX_NAME_COL_W,
    minWidth: MATRIX_NAME_COL_W,
    maxWidth: MATRIX_NAME_COL_W,
    flexShrink: 0,
    boxSizing: 'border-box',
    background: CINEV.card,
    borderRight: `1px solid ${CINEV.lineSoft}`,
  };

  return (
    <div
      ref={portRef}
      style={{
        position: 'relative', overflow: 'auto', flex: 1, minHeight: 0, width: '100%',
      }}
    >
      {matrixTip && matrixTip.text ? (
        <div
          role="tooltip"
          style={{
            position: 'fixed',
            left: matrixTip.x,
            top: matrixTip.y,
            zIndex: 50000,
            maxWidth: 280,
            padding: '8px 10px',
            background: CINEV.ink,
            color: '#fff',
            borderRadius: 8,
            fontSize: 11,
            lineHeight: 1.45,
            pointerEvents: 'none',
            boxShadow: '0 8px 24px rgba(0,0,0,0.2)',
            whiteSpace: 'pre-wrap',
            fontWeight: 400,
          }}
        >
          {matrixTip.text}
        </div>
      ) : null}
      <div style={{ minWidth: innerMinW, position: 'relative' }}>
        {highlightHour != null && (
          <div
            aria-hidden
            style={{
              position: 'absolute',
              left: MATRIX_NAME_COL_W + (highlightHour - MATRIX_DAY_START_HOUR) * MATRIX_COL_W,
              top: 0,
              bottom: 0,
              width: MATRIX_COL_W,
              boxSizing: 'border-box',
              borderLeft: `1px solid ${CINEV.teal}33`,
              borderRight: `1px solid ${CINEV.teal}33`,
              background: `${CINEV.teal}0a`,
              pointerEvents: 'none',
              zIndex: 1,
            }}
          />
        )}
        <div
          data-schedule-matrix-header
          style={{
            display: 'flex', borderBottom: `1px solid ${CINEV.lineSoft}`,
            fontSize: 10, color: CINEV.inkMute, letterSpacing: 0.5, textTransform: 'uppercase', background: CINEV.bg,
            position: 'sticky', top: 0, zIndex: 4,
          }}
        >
          <div style={{
            ...nameColSticky, zIndex: 6, top: 0, padding: '6px 8px 6px 18px', background: CINEV.bg,
          }}>{colLabel}</div>
          <div style={{ display: 'flex', width: timelineInnerW, flexShrink: 0 }}>
            {hours.map((hh) => {
              const isNowHour = highlightHour != null && parseInt(hh, 10) === highlightHour;
              return (
                <div
                  key={hh}
                  style={{
                    width: MATRIX_COL_W,
                    minWidth: MATRIX_COL_W,
                    boxSizing: 'border-box',
                    padding: '6px 0',
                    textAlign: 'left',
                    borderLeft: `1px dashed ${CINEV.lineSoft}`,
                    ...(isNowHour ? { color: CINEV.teal, fontWeight: 600 } : {}),
                  }}
                >
                  {hh}:00
                </div>
              );
            })}
          </div>
        </div>
        {allMatrixRows.map((row) => (
          <div key={row.id} style={{
            display: 'flex', position: 'relative', borderTop: `1px solid ${CINEV.lineSoft}`, minHeight: MATRIX_ROW_MIN_PX,
            zIndex: 2,
          }}>
            <div style={{
              ...nameColSticky, zIndex: 3, padding: '10px 12px 10px 18px', display: 'flex', alignItems: 'center', gap: 8,
            }}>
              {row.empty ? (
                <React.Fragment>
                  <div style={{ width: 32, height: 32, flexShrink: 0 }}/>
                  <div style={{ fontSize: 11.5, color: CINEV.inkMute }}>—</div>
                </React.Fragment>
              ) : usePatientAxis ? (
                <React.Fragment>
                  <PatientAvatar name={row.label} patientKey={row.id} size={32} />
                  <div style={{ fontSize: 11.5, fontWeight: 500, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>{row.label}</div>
                </React.Fragment>
              ) : (
                <React.Fragment>
                  <Avatar therapist={row.id} size={32} therapistApiMap={live ? therapistApiMap : undefined}/>
                  <div style={{ fontSize: 11.5, fontWeight: 500, whiteSpace: 'nowrap' }}>{row.name.split(' ').pop()}</div>
                </React.Fragment>
              )}
            </div>
            <div style={{ width: timelineInnerW, flexShrink: 0, position: 'relative', minHeight: MATRIX_ROW_MIN_PX }}>
              {hours.map((_, i) => (
                <div key={i} style={{
                  position: 'absolute', top: 0, bottom: 0, left: i * MATRIX_COL_W, width: MATRIX_COL_W,
                  borderLeft: `1px dashed ${CINEV.lineSoft}`, zIndex: 0, pointerEvents: 'none',
                }}/>
              ))}
              {!row.empty && live && usePatientAxis
                ? liveAppts.filter((a) => String(a.patient_id) === String(row.id) && a.status !== 'cancelled').map((a, i) => {
                  const { h, m } = typeof cinevWallHourMinuteFromIso === 'function'
                    ? cinevWallHourMinuteFromIso(a.start_at)
                    : (() => {
                      const d = new Date(a.start_at);
                      return { h: d.getHours(), m: d.getMinutes() };
                    })();
                  const x = ((h - MATRIX_DAY_START_HOUR) + m / 60) * MATRIX_COL_W;
                  const durMin = Math.max(15, Math.round((new Date(a.end_at) - new Date(a.start_at)) / 60000));
                  const w = Math.min(76, Math.max(26, (durMin / 60) * MATRIX_COL_W - 8));
                  const { color, bg, fg, st } = liveApptStyle(a);
                  const si = a.session_index != null ? Number(a.session_index) + 1 : '?';
                  const open = typeof onOpenPatientDetail === 'function' && a.patient_id != null;
                  const tipText = dashboardLiveApptTooltipLines(a, therapistApiMap);
                  return (
                    <div
                      key={a.id || i}
                      role={open ? 'button' : undefined}
                      tabIndex={open ? 0 : undefined}
                      onClick={() => open && onOpenPatientDetail(String(a.patient_id))}
                      onKeyDown={(e) => {
                        if (open && (e.key === 'Enter' || e.key === ' ')) {
                          e.preventDefault();
                          onOpenPatientDetail(String(a.patient_id));
                        }
                      }}
                      {...matrixTipHandlers(tipText)}
                      aria-label={tipText.replace(/\n/g, ', ')}
                      style={{ position: 'absolute', top: 5, bottom: 5, left: x + 2, width: w, zIndex: 2,
                      background: bg, borderLeft: `3px solid ${color}`, borderRadius: 3, padding: '3px 6px', boxSizing: 'border-box',
                      fontSize: 10, color: fg, overflow: 'hidden', border: st !== 'now' ? `1px solid ${CINEV.line}` : 'none',
                      cursor: open ? 'pointer' : 'default' }}
                    >
                      <div style={{ fontWeight: 600, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>{liveTherapistShort(a)}</div>
                      <div style={{ opacity: 0.7, fontSize: 9, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>
                        {`${si}/12 · ${liveTypeShort(a)}`}
                      </div>
                    </div>
                  );
                })
                : null}
              {!row.empty && live && !usePatientAxis
                ? liveAppts.filter((a) => String(a.therapist_id) === String(row.id) && a.status !== 'cancelled').map((a, i) => {
                  const { h, m } = typeof cinevWallHourMinuteFromIso === 'function'
                    ? cinevWallHourMinuteFromIso(a.start_at)
                    : (() => {
                      const d = new Date(a.start_at);
                      return { h: d.getHours(), m: d.getMinutes() };
                    })();
                  const x = ((h - MATRIX_DAY_START_HOUR) + m / 60) * MATRIX_COL_W;
                  const durMin = Math.max(15, Math.round((new Date(a.end_at) - new Date(a.start_at)) / 60000));
                  const w = Math.min(76, Math.max(26, (durMin / 60) * MATRIX_COL_W - 8));
                  const { color, bg, fg, st } = liveApptStyle(a);
                  const firstName = (a.patient_name || '').split(/\s+/)[0] || '—';
                  const si = a.session_index != null ? Number(a.session_index) + 1 : '?';
                  const open = typeof onOpenPatientDetail === 'function' && a.patient_id != null;
                  const tipText = dashboardLiveApptTooltipLines(a, therapistApiMap);
                  return (
                    <div
                      key={a.id || i}
                      role={open ? 'button' : undefined}
                      tabIndex={open ? 0 : undefined}
                      onClick={() => open && onOpenPatientDetail(String(a.patient_id))}
                      onKeyDown={(e) => {
                        if (open && (e.key === 'Enter' || e.key === ' ')) {
                          e.preventDefault();
                          onOpenPatientDetail(String(a.patient_id));
                        }
                      }}
                      {...matrixTipHandlers(tipText)}
                      aria-label={tipText.replace(/\n/g, ', ')}
                      style={{ position: 'absolute', top: 5, bottom: 5, left: x + 2, width: w, zIndex: 2,
                      background: bg, borderLeft: `3px solid ${color}`, borderRadius: 3, padding: '3px 6px', boxSizing: 'border-box',
                      fontSize: 10, color: fg, overflow: 'hidden', border: st !== 'now' ? `1px solid ${CINEV.line}` : 'none',
                      cursor: open ? 'pointer' : 'default' }}
                    >
                      <div style={{ fontWeight: 600, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>{firstName}</div>
                      <div style={{ opacity: 0.7, fontSize: 9, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>
                        {`${si}/12 · ${liveTypeShort(a)}`}
                      </div>
                    </div>
                  );
                })
                : null}
            </div>
          </div>
        ))}
      </div>
    </div>
  );
}

function KPI({ label, value, sub, accent }) {
  return (
    <div style={{ background: accent ? CINEV.teal : CINEV.card, color: accent ? '#fff' : CINEV.ink,
      borderRadius: 10, padding: '12px 14px', border: accent ? 'none' : `1px solid ${CINEV.line}` }}>
      <div style={{ fontSize: 10.5, opacity: accent ? 0.75 : 1, color: accent ? '#fff' : CINEV.inkMute, letterSpacing: 0.5, textTransform: 'uppercase', fontWeight: 500 }}>{label}</div>
      <div style={{ fontSize: 28, fontWeight: 600, letterSpacing: -0.8, margin: '4px 0 2px', fontVariantNumeric: 'tabular-nums' }}>{value}</div>
      <div style={{ fontSize: 11, opacity: accent ? 0.85 : 1, color: accent ? '#fff' : CINEV.inkMute }}>{sub}</div>
    </div>
  );
}

function Legend({ color, label }) {
  return (<div style={{ display: 'flex', alignItems: 'center', gap: 5 }}>
    <div style={{ width: 8, height: 8, borderRadius: 2, background: color }}/>{label}</div>);
}

function StatusPill({ status }) {
  const map = {
    running: [CINEV.teal, '#fff', t('status_running')],
    planned: [CINEV.soft, CINEV.teal, t('status_planned')],
    mail: [CINEV.warn + '22', CINEV.warn, t('status_mail')],
    almost: [CINEV.greenDark + '22', CINEV.greenDark, t('status_almost')],
  };
  const [bg, fg, label] = map[status] || [CINEV.soft, CINEV.inkSoft, status];
  return (<span style={{ fontSize: 10.5, padding: '3px 8px', borderRadius: 10,
    background: bg, color: fg, fontWeight: 600, whiteSpace: 'nowrap' }}>{label}</span>);
}

function Th({ children }) {
  return (
    <th style={{
      textAlign: 'left', padding: '11px 12px', fontWeight: 600, fontSize: 10.5,
      letterSpacing: 0.5, textTransform: 'uppercase', color: 'inherit',
    }}>{children}</th>
  );
}

function btn(variant) {
  if (variant === 'primary') return { background: CINEV.teal, color: '#fff', border: 'none',
    padding: '8px 14px', borderRadius: 6, fontSize: 12.5, fontWeight: 500, cursor: 'pointer', fontFamily: 'inherit' };
  return { background: 'transparent', color: CINEV.inkSoft, border: `1px solid ${CINEV.line}`,
    padding: '7px 13px', borderRadius: 6, fontSize: 12.5, cursor: 'pointer', fontFamily: 'inherit' };
}

Object.assign(window, { Dashboard, TodayView, ScheduleMatrix, KPI, Legend, StatusPill, Th, btn });
