const state = {
  token: localStorage.getItem('token'),
  user: localStorage.getItem('token') ? JSON.parse(localStorage.getItem('user') || 'null') : null,
  tournaments: [],
  route: 'public',
  ws: null,
  scoreMatchId: null,
  scoreMode: localStorage.getItem('scoreMode') || 'expert',
  sidebarCollapsed: localStorage.getItem('sidebarCollapsed') === '1',
  authValidated: false
};

const $ = (selector) => document.querySelector(selector);
const view = $('#view');

function toast(message, kind = 'ok') {
  const item = document.createElement('div');
  item.className = `rounded px-4 py-3 text-sm font-bold shadow ${kind === 'error' ? 'bg-red-600' : 'bg-zinc-900'} text-white`;
  item.textContent = message;
  $('#toast').appendChild(item);
  setTimeout(() => item.remove(), 3200);
}

async function api(path, options = {}) {
  const headers = { 'Content-Type': 'application/json', ...(options.headers || {}) };
  if (state.token) headers.Authorization = `Bearer ${state.token}`;
  const res = await fetch(path, { ...options, headers });
  const data = await res.json().catch(() => ({}));
  if (!res.ok) {
    const error = new Error(data.message || 'Error de API');
    error.status = res.status;
    throw error;
  }
  return data;
}

function selectedTournamentId() {
  return Number($('#tournamentSelect').value || state.tournaments[0]?.id || 1);
}

function applySidebarState() {
  document.body.classList.toggle('sidebar-collapsed', state.sidebarCollapsed);
  $('#sidebarToggle')?.setAttribute('title', state.sidebarCollapsed ? 'Expandir menu' : 'Minimizar menu');
}

function openMobileMenu() {
  document.body.classList.add('mobile-menu-open');
}

function closeMobileMenu() {
  document.body.classList.remove('mobile-menu-open');
}

async function loadBase() {
  const tournaments = await api('/api/tournaments');
  state.tournaments = tournaments.data || [];
  $('#tournamentSelect').innerHTML = state.tournaments.map(t => `<option value="${t.id}">${t.name}</option>`).join('');
  updateSession();
}

async function validateStoredSession() {
  if (!state.token) {
    clearSession();
    state.authValidated = true;
    return;
  }

  try {
    const res = await api('/api/auth/me');
    state.user = res.user;
    localStorage.setItem('user', JSON.stringify(res.user));
  } catch (err) {
    if (err.status === 401 || err.status === 403) {
      clearSession();
    }
  } finally {
    state.authValidated = true;
    updateSession();
  }
}

function clearSession() {
  state.token = null;
  state.user = null;
  localStorage.removeItem('token');
  localStorage.removeItem('user');
}

function currentRole() {
  return String(state.user?.role || '').trim().toLowerCase();
}

function isAdmin() {
  return Boolean(state.token && state.user && currentRole() === 'admin');
}

function updateSession() {
  const role = currentRole();
  $('#sessionInfo').textContent = state.user ? `${state.user.name} - ${role}` : 'Modo publico';
  $('#loginBtn').setAttribute('title', state.user ? `Sesion: ${state.user.name}` : 'Ingresar');
  $('#sideLoginBtn').textContent = state.user ? 'Ver sesion' : 'Ingresar';
  $('#sessionUser').textContent = state.user ? state.user.name : 'Invitado';
  $('#sessionRole').textContent = state.user ? `${state.user.email} - ${role}` : 'Modo publico';
}

function openModal({ eyebrow = '', title, subtitle = '', body, footer = '' }) {
  $('#modalEyebrow').textContent = eyebrow;
  $('#modalTitle').textContent = title;
  $('#modalSubtitle').textContent = subtitle;
  $('#modalBody').innerHTML = body;
  $('#modalFooter').innerHTML = footer;
  $('#modalFooter').classList.toggle('hidden', !footer);
  $('#modalRoot').classList.remove('hidden');
  $('#modalRoot').setAttribute('aria-hidden', 'false');
  document.body.style.overflow = 'hidden';
}

function closeModal() {
  $('#modalRoot').classList.add('hidden');
  $('#modalRoot').setAttribute('aria-hidden', 'true');
  document.body.style.overflow = '';
}

function openLoginModal() {
  if (state.user) {
    openModal({
      eyebrow: 'Sesion activa',
      title: state.user.name,
      subtitle: `${state.user.email} - ${state.user.role}`,
      body: `<div class="space-y-3">
        <p class="text-sm text-slate-500 dark:text-zinc-400">Ya estas autenticado en el sistema.</p>
        <button id="logoutBtn" class="btn-muted w-full" type="button">Cerrar sesion</button>
      </div>`
    });
    $('#logoutBtn').onclick = () => {
      clearSession();
      updateSession();
      closeModal();
      toast('Sesion cerrada');
      render();
    };
    return;
  }

  openModal({
    eyebrow: 'Acceso seguro',
    title: 'Ingresar',
    subtitle: 'Usa tu usuario administrador o delegado para gestionar torneos.',
    body: `<div class="login-panel mb-4">
      <span class="login-badge">JWT activo</span>
      <p class="mt-3 text-sm text-slate-600 dark:text-zinc-300">Credenciales de prueba: admin@volley.test / password</p>
    </div>
    <form id="loginForm" class="space-y-4">
      <label class="field"><span class="field-label">Email</span>
      <input class="input" name="email" value="admin@volley.test" autocomplete="email" placeholder="Email">
      </label>
      <label class="field"><span class="field-label">Contrasena</span>
      <input class="input" name="password" value="password" type="password" autocomplete="current-password" placeholder="Contrasena">
      </label>
      <button class="btn-primary w-full" type="submit">Ingresar</button>
    </form>`
  });

  const loginForm = $('#modalBody').querySelector('#loginForm');
  if (loginForm) {
    loginForm.onsubmit = handleLogin;
  }
}

async function handleLogin(e) {
  e.preventDefault();
  const data = Object.fromEntries(new FormData(e.target));
  const res = await api('/api/auth/login', { method: 'POST', body: JSON.stringify(data) });
  state.token = res.token;
  state.user = res.user;
  state.authValidated = true;
  localStorage.setItem('token', res.token);
  localStorage.setItem('user', JSON.stringify(res.user));
  updateSession();
  closeModal();
  toast('Sesion iniciada');
  render();
}

function setRoute(route) {
  state.route = route;
  syncActiveRoute();
  closeMobileMenu();
  render().catch(err => toast(err.message, 'error'));
}

function syncActiveRoute() {
  document.querySelectorAll('.nav-btn').forEach(btn => btn.classList.toggle('active', btn.dataset.route === state.route));
}

function toggleSidebar() {
  state.sidebarCollapsed = !state.sidebarCollapsed;
  localStorage.setItem('sidebarCollapsed', state.sidebarCollapsed ? '1' : '0');
  applySidebarState();
}

async function render() {
  try {
    syncActiveRoute();
    if (state.route === 'admin') return renderAdmin();
    if (state.route === 'score') return renderScore();
    if (state.route === 'team-link') return renderTeamLink();
    return renderPublic();
  } catch (err) {
    toast(err.message, 'error');
  }
}

async function renderPublic() {
  const id = selectedTournamentId();
  const [standings, matches, stats] = await Promise.all([
    api(`/api/tournaments/${id}/standings`),
    api(`/api/matches?tournament_id=${id}&per_page=50`),
    api(`/api/tournaments/${id}/stats`)
  ]);
  view.innerHTML = `
    <div class="grid gap-4 xl:grid-cols-2">
      <section class="panel xl:col-span-2">
        <h1 class="text-2xl font-black">Resultados, fixture y tabla</h1>
        <p class="text-sm text-slate-500">Vista publica responsive con datos actualizados desde la planilla electronica.</p>
      </section>
      <section class="panel">
        <h2 class="section-title">Tabla de posiciones</h2>
        ${table(['Equipo','PJ','G','P','SF','SC','Dif','Pts'], standings.map(r => [r.name, r.played, r.won, r.lost, r.sets_for, r.sets_against, r.sets_for - r.sets_against, r.points]))}
      </section>
      <section class="panel">
        <h2 class="section-title">Calendario</h2>
        ${table(['Fecha','Local','Visitante','Sets','Estado'], (matches.data || []).map(m => [fmt(m.scheduled_at), m.home_team, m.away_team, `${m.home_sets}-${m.away_sets}`, m.status]))}
      </section>
      <section class="panel xl:col-span-2">
        <h2 class="section-title">Ranking de jugadores</h2>
        ${table(['Jugador','Equipo','Puntos','Aces','Bloqueos','MVP'], (stats.players || []).map(p => [p.player_name, p.team_name, p.points || 0, p.aces || 0, p.blocks || 0, p.mvp || 0]))}
      </section>
    </div>`;
}

async function renderAdmin() {
  if (!state.authValidated || state.token) {
    await validateStoredSession();
  }

  if (!isAdmin()) {
    view.innerHTML = `<section class="panel max-w-xl">
      <h1 class="text-2xl font-black">Acceso administrador</h1>
      <p class="mt-2 text-sm text-slate-500">Para crear torneos, equipos y partidos tenes que iniciar sesion con un usuario administrador.</p>
      <p class="mt-2 text-xs text-slate-500">Estado actual: ${state.user ? `${state.user.email} - ${currentRole() || 'sin rol'}` : 'sin sesion valida'}</p>
      <button class="btn-primary mt-4" id="adminLoginCta">Ingresar</button>
    </section>`;
    $('#adminLoginCta').onclick = openLoginModal;
    setTimeout(openLoginModal, 150);
    return;
  }

  const id = selectedTournamentId();
  let teams;
  let players;
  let matches;
  let users;
  let templates;
  try {
    [teams, players, matches, users, templates] = await Promise.all([
      api(`/api/teams?tournament_id=${id}&per_page=50`),
      api('/api/players?per_page=50'),
      api(`/api/matches?tournament_id=${id}&per_page=50`),
      api('/api/users?per_page=50'),
      api(`/api/sheet-templates?per_page=50&tournament_id=${id}`)
    ]);
  } catch (err) {
    if (err.status === 401) {
      clearSession();
      state.authValidated = true;
      updateSession();
      toast('La sesion expiro. Inicia sesion nuevamente.', 'error');
      return renderAdmin();
    }
    throw err;
  }
  view.innerHTML = `
    <div class="grid gap-4">
      <section class="panel">
        <div class="flex flex-wrap items-end justify-between gap-4">
          <div>
            <h1 class="text-2xl font-black">Dashboard administrativo</h1>
            <p class="text-sm text-slate-500">Gestion operativa del torneo seleccionado.</p>
          </div>
          <div class="grid grid-cols-2 gap-2 sm:grid-cols-4">
            ${metricCard('Equipos', teams.data?.length || 0)}
            ${metricCard('Jugadores', players.data?.length || 0)}
            ${metricCard('Partidos', matches.data?.length || 0)}
            ${metricCard('Usuarios', users.data?.length || 0)}
          </div>
        </div>
      </section>
      <section class="dashboard-actions">
        <button class="action-tile" data-admin-modal="user">
          <span class="action-icon">+</span>
          <span class="mt-4 block text-base font-black">Crear usuario</span>
          <span class="mt-1 block text-sm text-slate-500">Admin o delegado.</span>
        </button>
        <button class="action-tile" data-admin-modal="tournament">
          <span class="action-icon">+</span>
          <span class="mt-4 block text-base font-black">Crear torneo</span>
          <span class="mt-1 block text-sm text-slate-500">Categoria, formato y fechas.</span>
        </button>
        <button class="action-tile" data-admin-modal="team">
          <span class="action-icon">+</span>
          <span class="mt-4 block text-base font-black">Crear equipo</span>
          <span class="mt-1 block text-sm text-slate-500">Nombre, DT y link de ficha.</span>
        </button>
        <button class="action-tile" data-admin-modal="match">
          <span class="action-icon">+</span>
          <span class="mt-4 block text-base font-black">Crear partido</span>
          <span class="mt-1 block text-sm text-slate-500">Equipos, fecha y cancha.</span>
        </button>
      <button class="action-tile" data-admin-modal="template">
        <span class="action-icon">+</span>
        <span class="mt-4 block text-base font-black">Crear plantilla</span>
        <span class="mt-1 block text-sm text-slate-500">Imagen y coordenadas.</span>
      </button>
      <button class="action-tile" id="demoScoresheetBtn">
        <span class="action-icon">+</span>
        <span class="mt-4 block text-base font-black">Demo planilla</span>
        <span class="mt-1 block text-sm text-slate-500">Datos para probar export.</span>
      </button>
      </section>
      <section class="dashboard-panels">
        <section class="panel"><h2 class="section-title">Equipos</h2>${table(['Equipo','DT','Link ficha'], (teams.data || []).map(t => [t.name, t.coach_name || '-', `/registro/${t.registration_token}`]))}</section>
        <section class="panel"><h2 class="section-title">Usuarios</h2>${userTable(users.data || [])}</section>
        <section class="panel"><h2 class="section-title">Plantillas de planilla</h2>${templateTable(templates.data || [])}</section>
        <section class="panel"><h2 class="section-title">Jugadores</h2>${table(['Jugador','Equipo','DNI','Nro','Posicion'], (players.data || []).map(p => [`${p.first_name} ${p.last_name}`, p.team_name, p.document_id, p.jersey_number || '-', p.position || '-']))}</section>
        <section class="panel"><h2 class="section-title">Partidos</h2>${table(['ID','Fecha','Local','Visitante','Estado'], (matches.data || []).map(m => [m.id, fmt(m.scheduled_at), m.home_team, m.away_team, m.status]))}</section>
      </section>
    </div>`;
  bindAdminActions(teams.data || [], users.data || [], templates.data || []);
}

function bindAdminActions(teams, users, templates) {
  document.querySelector('[data-admin-modal="user"]').onclick = () => openUserModal();
  document.querySelector('[data-admin-modal="tournament"]').onclick = () => openTournamentModal();
  document.querySelector('[data-admin-modal="team"]').onclick = () => openTeamModal();
  document.querySelector('[data-admin-modal="match"]').onclick = () => openMatchModal(teams);
  document.querySelector('[data-admin-modal="template"]').onclick = () => openTemplateModal();
  $('#demoScoresheetBtn').onclick = openDemoScoresheetModal;
  document.querySelectorAll('[data-edit-user]').forEach(btn => {
    btn.onclick = () => openUserModal(users.find(user => Number(user.id) === Number(btn.dataset.editUser)));
  });
  document.querySelectorAll('[data-toggle-user]').forEach(btn => {
    btn.onclick = () => toggleUser(Number(btn.dataset.toggleUser), Number(btn.dataset.active) ? 0 : 1);
  });
  document.querySelectorAll('[data-assign-template]').forEach(btn => {
    btn.onclick = () => openAssignTemplateModal(Number(btn.dataset.assignTemplate));
  });
  document.querySelectorAll('[data-edit-template]').forEach(btn => {
    btn.onclick = () => openTemplateDesigner(Number(btn.dataset.editTemplate));
  });
}

function openUserModal(user = null) {
  const isEdit = Boolean(user);
  openModal({
    eyebrow: 'Seguridad',
    title: isEdit ? 'Editar usuario' : 'Crear usuario',
    subtitle: 'Administra accesos para administradores y delegados.',
    body: `<form id="userForm" class="space-y-4">
      ${field('Nombre', input('name','Nombre', 'text', user?.name || ''))}
      ${field('Email', input('email','Email', 'email', user?.email || ''))}
      ${field(isEdit ? 'Nueva contrasena opcional' : 'Contrasena', input('password','Contrasena', 'password'))}
      ${field('Rol', `<select class="input" name="role">
        <option value="admin" ${user?.role === 'admin' ? 'selected' : ''}>admin</option>
        <option value="delegate" ${user?.role === 'delegate' ? 'selected' : ''}>delegate</option>
        <option value="public" ${user?.role === 'public' ? 'selected' : ''}>public</option>
      </select>`)}
      ${field('Estado', `<select class="input" name="active">
        <option value="1" ${!user || Number(user.active) ? 'selected' : ''}>Activo</option>
        <option value="0" ${user && !Number(user.active) ? 'selected' : ''}>Inactivo</option>
      </select>`)}
      <button class="btn-primary w-full">${isEdit ? 'Guardar cambios' : 'Crear usuario'}</button>
    </form>`
  });

  $('#userForm').onsubmit = submitForm(isEdit ? `/api/users/${user.id}` : '/api/users', data => {
    if (isEdit && !data.password) delete data.password;
    return data;
  }, true, isEdit ? 'PUT' : 'POST');
}

async function toggleUser(id, active) {
  await api(`/api/users/${id}`, { method: 'PUT', body: JSON.stringify({ active }) });
  toast(active ? 'Usuario activado' : 'Usuario desactivado');
  render();
}

function openTemplateModal() {
  const defaultConfig = JSON.stringify({
    fields: {},
    result_rows: [],
    result_columns: {},
    home_players: {},
    away_players: {}
  }, null, 2);
  openModal({
    eyebrow: 'Plantillas',
    title: 'Crear plantilla',
    subtitle: 'Subi la imagen a public/templates y defini coordenadas JSON.',
    body: `<form id="templateForm" class="space-y-4">
      ${field('Codigo', input('code','ej: ltv27'))}
      ${field('Nombre', input('name','Nombre de plantilla'))}
      ${field('Ruta imagen', input('image_path','/templates/mi-planilla.png'))}
      ${field('Ancho px', input('page_width','1418','number'))}
      ${field('Alto px', input('page_height','970','number'))}
      ${field('Config JSON', `<textarea class="input min-h-[180px] font-mono text-xs" name="config_json">${escapeHtml(defaultConfig)}</textarea>`)}
      <button class="btn-primary w-full">Crear plantilla</button>
    </form>`
  });
  $('#templateForm').onsubmit = submitForm('/api/sheet-templates', data => ({ ...data, config_json: JSON.parse(data.config_json) }), true);
}

function openAssignTemplateModal(templateId) {
  openModal({
    eyebrow: 'Plantillas',
    title: 'Asignar plantilla',
    subtitle: 'Selecciona el torneo que usara esta planilla por defecto.',
    body: `<form id="assignTemplateForm" class="space-y-4">
      ${field('Torneo', `<select class="input" name="tournament_id">${state.tournaments.map(tournament => `<option value="${tournament.id}" ${Number(tournament.id) === selectedTournamentId() ? 'selected' : ''}>${tournament.name}</option>`).join('')}</select>`)}
      <button class="btn-primary w-full">Asignar plantilla</button>
    </form>`
  });
  $('#assignTemplateForm').onsubmit = async (event) => {
    event.preventDefault();
    const data = Object.fromEntries(new FormData(event.target));
    await assignTemplateToTournament(templateId, Number(data.tournament_id));
    closeModal();
  };
}

async function assignTemplateToTournament(templateId, tournamentId) {
  try {
    await api(`/api/tournaments/${tournamentId}/sheet-template`, { method: 'POST', body: JSON.stringify({ sheet_template_id: templateId }) });
    toast('Plantilla asignada al torneo');
    await renderAdmin();
  } catch (err) {
    toast(err.message, 'error');
    await renderAdmin();
  }
}

function openDemoScoresheetModal() {
  openModal({
    eyebrow: 'Demo',
    title: 'Generar demo de planilla',
    subtitle: 'Crea equipos, jugadores, partido, sets, libero, rotaciones y firma demo para probar la plantilla.',
    body: `<form id="demoScoresheetForm" class="space-y-4">
      ${field('Torneo', `<select class="input" name="tournament_id">${state.tournaments.map(tournament => `<option value="${tournament.id}" ${Number(tournament.id) === selectedTournamentId() ? 'selected' : ''}>${tournament.name}</option>`).join('')}</select>`)}
      ${field('Modo', '<select class="input" name="force"><option value="0">No pisar demo existente</option><option value="1">Forzar regeneracion</option></select>')}
      <button class="btn-primary w-full">Generar demo</button>
    </form>`
  });
  $('#demoScoresheetForm').onsubmit = async (event) => {
    event.preventDefault();
    const data = Object.fromEntries(new FormData(event.target));
    const res = await api(`/api/tournaments/${data.tournament_id}/demo-scoresheet-data`, { method: 'POST', body: JSON.stringify({ force: Number(data.force) === 1 }) });
    closeModal();
    toast(res.created ? `Demo creado. Partido #${res.match_id}` : res.message);
    await renderAdmin();
  };
}

async function openTemplateDesigner(templateId) {
  const template = await api(`/api/sheet-templates/${templateId}/effective`);
  if (!template) return;
  const config = safeJson(template.effective_config_json || template.config_json, {});
  state.designer = {
    template,
    config,
    selectedKey: null,
    selectedKind: 'field',
    drag: null
  };
  renderTemplateDesigner();
}

function renderTemplateDesigner() {
  const template = state.designer.template;
  const config = state.designer.config;
  view.innerHTML = `<div class="grid gap-4">
    <section class="panel">
      <div class="flex flex-wrap items-center justify-between gap-3">
        <div>
          <h1 class="text-2xl font-black">Editor de plantilla</h1>
          <p class="text-sm text-slate-500">${template.name} - ${template.page_width}x${template.page_height}</p>
        </div>
        <div class="flex gap-2">
          <button class="btn-muted" id="backAdminBtn">Volver</button>
          <button class="btn-primary" id="saveTemplateDesignBtn">Guardar diseno</button>
        </div>
      </div>
    </section>
    <section class="template-editor">
      <div class="template-canvas-wrap">
        <div id="templateCanvas" class="template-canvas" tabindex="0" style="aspect-ratio:${template.page_width}/${template.page_height}">
          <img src="${template.image_path}" alt="${template.name}" tabindex="-1">
          ${designerFields(config)}
          ${designerPlayerBlock(config.home_players, 'home_players', 'Jugadores A')}
          ${designerPlayerBlock(config.away_players, 'away_players', 'Jugadores B')}
          ${designerResultBlock(config)}
        </div>
      </div>
      <aside class="template-side">
        <section class="panel">
          <h2 class="section-title">Agregar campo</h2>
          <div class="template-field-list">
            ${availableTemplateFields().map(key => `<button class="btn-muted text-xs" data-add-field="${key}">${key}</button>`).join('')}
            <button class="btn-muted text-xs" data-select-block="home_players">Bloque jugadores A</button>
            <button class="btn-muted text-xs" data-select-block="away_players">Bloque jugadores B</button>
            <button class="btn-muted text-xs" data-select-block="result">Bloque resultado</button>
          </div>
        </section>
        <section class="panel">
          <h2 class="section-title">Campo seleccionado</h2>
          <div id="fieldProps">${fieldPropsPanel()}</div>
        </section>
      </aside>
    </section>
  </div>`;
  bindTemplateDesigner();
}

function designerFields(config) {
  return Object.entries(config.fields || {}).map(([key, box]) => {
    const left = percent(box.x, state.designer.template.page_width);
    const top = percent(box.y, state.designer.template.page_height);
    const width = percent(box.w || 120, state.designer.template.page_width);
    const active = state.designer.selectedKey === key ? 'active' : '';
    return `<div class="template-field ${active}" data-field-key="${key}" style="left:${left}%;top:${top}%;width:${width}%;font-size:${box.size || 12}px;justify-content:${alignToJustify(box.align)};align-items:${verticalToAlign(box.valign)};transform:${verticalToTransform(box.valign)}">${fieldPreview(key)}</div>`;
  }).join('');
}

function designerPlayerBlock(config, key, label) {
  if (!config?.x) return '';
  const left = percent(config.x, state.designer.template.page_width);
  const top = percent(config.y, state.designer.template.page_height);
  const width = percent((config.number_w || 30) + (config.name_w || 120), state.designer.template.page_width);
  const height = percent((config.row_h || 20) * 14, state.designer.template.page_height);
  const active = state.designer.selectedKey === key ? 'active' : '';
  return `<div class="template-field ${active}" data-block-key="${key}" style="left:${left}%;top:${top}%;width:${width}%;height:${height}%;font-size:${config.size || 10}px;align-items:${verticalToAlign(config.valign)}">${label}</div>`;
}

function designerResultBlock(config) {
  if (!config?.result_rows?.length || !config?.result_columns?.set) return '';
  const row = config.result_rows[0];
  const col = config.result_columns.home_t || config.result_columns.set;
  const lastRow = config.result_rows[config.result_rows.length - 1];
  const left = percent(col.x, state.designer.template.page_width);
  const top = percent(row.y, state.designer.template.page_height);
  const width = percent(300, state.designer.template.page_width);
  const height = percent((lastRow.y - row.y) + 24, state.designer.template.page_height);
  const active = state.designer.selectedKey === 'result' ? 'active' : '';
  return `<div class="template-field ${active}" data-block-key="result" style="left:${left}%;top:${top}%;width:${width}%;height:${height}%;font-size:11px">Resultado</div>`;
}

function fieldPropsPanel() {
  const key = state.designer?.selectedKey;
  if (!key) return '<p class="text-sm text-slate-500">Selecciona un campo del lienzo.</p>';
  if (state.designer.selectedKind === 'block') {
    return blockPropsPanel(key);
  }
  const box = state.designer.config.fields[key];
  return `<div class="space-y-3">
    ${field('Campo', `<input class="input" value="${escapeAttr(key)}" disabled>`)}
    ${field('X', input('prop_x','x','number', box.x || 0))}
    ${field('Y', input('prop_y','y','number', box.y || 0))}
    ${field('Ancho', input('prop_w','w','number', box.w || 120))}
    ${field('Fuente', input('prop_size','size','number', box.size || 12))}
    ${field('Alineacion', `<select class="input" name="prop_align"><option value="left" ${box.align === 'left' ? 'selected' : ''}>left</option><option value="center" ${box.align === 'center' ? 'selected' : ''}>center</option><option value="right" ${box.align === 'right' ? 'selected' : ''}>right</option></select>`)}
    ${field('Alineacion vertical', `<select class="input" name="prop_valign"><option value="top" ${box.valign === 'top' ? 'selected' : ''}>top</option><option value="middle" ${box.valign === 'middle' ? 'selected' : ''}>middle</option><option value="bottom" ${box.valign === 'bottom' ? 'selected' : ''}>bottom</option></select>`)}
    <button class="btn-muted w-full" id="deleteFieldBtn">Eliminar campo</button>
  </div>`;
}

function blockPropsPanel(key) {
  const block = getDesignerBlock(key);
  if (!block) return '<p class="text-sm text-slate-500">Bloque no disponible.</p>';
  return `<div class="space-y-3">
    ${field('Bloque', `<input class="input" value="${escapeAttr(key)}" disabled>`)}
    ${field('X', input('block_x','x','number', block.x || 0))}
    ${field('Y', input('block_y','y','number', block.y || 0))}
    ${key === 'result' ? field('Separacion filas', input('block_row_h','row_h','number', resultRowHeight())) : field('Alto fila', input('block_row_h','row_h','number', block.row_h || 22))}
    ${key === 'result' ? '' : field('Ancho numero', input('block_number_w','number_w','number', block.number_w || 32))}
    ${key === 'result' ? '' : field('Ancho nombre', input('block_name_w','name_w','number', block.name_w || 124))}
    ${field('Fuente', input('block_size','size','number', block.size || 10))}
    ${field('Alineacion vertical', `<select class="input" name="block_valign"><option value="top" ${block.valign === 'top' ? 'selected' : ''}>top</option><option value="middle" ${block.valign === 'middle' ? 'selected' : ''}>middle</option><option value="bottom" ${block.valign === 'bottom' ? 'selected' : ''}>bottom</option></select>`)}
    <button class="btn-muted w-full" id="deleteBlockBtn">Eliminar bloque</button>
  </div>`;
}

function bindTemplateDesigner() {
  $('#backAdminBtn').onclick = () => {
    document.onkeydown = null;
    renderAdmin();
  };
  $('#saveTemplateDesignBtn').onclick = saveTemplateDesign;
  document.querySelectorAll('[data-add-field]').forEach(btn => btn.onclick = () => addTemplateField(btn.dataset.addField));
  document.querySelectorAll('[data-select-block]').forEach(btn => btn.onclick = () => selectTemplateBlock(btn.dataset.selectBlock));
  document.querySelectorAll('[data-field-key]').forEach(el => {
    el.onclick = () => selectTemplateField(el.dataset.fieldKey);
    el.onpointerdown = (event) => startFieldDrag(event, el.dataset.fieldKey);
  });
  document.querySelectorAll('[data-block-key]').forEach(el => {
    el.onclick = () => selectTemplateBlock(el.dataset.blockKey);
    el.onpointerdown = (event) => startBlockDrag(event, el.dataset.blockKey);
  });
  const props = $('#fieldProps');
  props.querySelectorAll('input, select').forEach(inputEl => inputEl.oninput = state.designer.selectedKind === 'block' ? updateSelectedBlockFromProps : updateSelectedFieldFromProps);
  const deleteBtn = $('#deleteFieldBtn');
  if (deleteBtn) deleteBtn.onclick = deleteSelectedField;
  const deleteBlockBtn = $('#deleteBlockBtn');
  if (deleteBlockBtn) deleteBlockBtn.onclick = deleteSelectedBlock;
  document.onkeydown = handleTemplateKeydown;
}

function addTemplateField(key) {
  if (['home_players', 'away_players', 'result'].includes(key)) {
    addTemplateBlock(key);
    return;
  }
  state.designer.config.fields = state.designer.config.fields || {};
  state.designer.config.fields[key] = state.designer.config.fields[key] || { x: 40, y: 40, w: 160, size: 12, align: 'left' };
  state.designer.selectedKey = key;
  state.designer.selectedKind = 'field';
  renderTemplateDesigner();
}

function selectTemplateField(key) {
  state.designer.selectedKey = key;
  state.designer.selectedKind = 'field';
  renderTemplateDesigner();
  focusTemplateCanvas();
}

function selectTemplateBlock(key) {
  if (!getDesignerBlock(key)) addTemplateBlock(key);
  state.designer.selectedKey = key;
  state.designer.selectedKind = 'block';
  renderTemplateDesigner();
  focusTemplateCanvas();
}

function addTemplateBlock(key) {
  if (key === 'home_players') state.designer.config.home_players = state.designer.config.home_players || { x: 1081, y: 548, row_h: 22, number_w: 32, name_w: 124, size: 10 };
  if (key === 'away_players') state.designer.config.away_players = state.designer.config.away_players || { x: 1238, y: 548, row_h: 22, number_w: 32, name_w: 124, size: 10 };
  if (key === 'result') {
    state.designer.config.result_rows = state.designer.config.result_rows || [{ y: 790 }, { y: 816 }, { y: 842 }, { y: 867 }, { y: 893 }];
    state.designer.config.result_columns = state.designer.config.result_columns || {
      home_pt: { x: 886, w: 34 },
      set: { x: 921, w: 70 },
      away_pt: { x: 992, w: 34 }
    };
  }
  state.designer.selectedKey = key;
  state.designer.selectedKind = 'block';
}

function startFieldDrag(event, key) {
  event.preventDefault();
  const canvas = $('#templateCanvas');
  const rect = canvas.getBoundingClientRect();
  const box = state.designer.config.fields[key];
  state.designer.selectedKey = key;
  state.designer.drag = {
    key,
    startX: event.clientX,
    startY: event.clientY,
    originX: Number(box.x || 0),
    originY: Number(box.y || 0),
    scaleX: state.designer.template.page_width / rect.width,
    scaleY: state.designer.template.page_height / rect.height
  };
  window.onpointermove = moveFieldDrag;
  window.onpointerup = stopFieldDrag;
}

function startBlockDrag(event, key) {
  event.preventDefault();
  const canvas = $('#templateCanvas');
  const rect = canvas.getBoundingClientRect();
  const box = getDesignerBlock(key);
  state.designer.selectedKey = key;
  state.designer.selectedKind = 'block';
  state.designer.drag = {
    key,
    kind: 'block',
    startX: event.clientX,
    startY: event.clientY,
    originX: Number(box.x || 0),
    originY: Number(box.y || 0),
    resultOriginColumns: key === 'result' ? clone(state.designer.config.result_columns || {}) : null,
    scaleX: state.designer.template.page_width / rect.width,
    scaleY: state.designer.template.page_height / rect.height
  };
  window.onpointermove = moveFieldDrag;
  window.onpointerup = stopFieldDrag;
}

function moveFieldDrag(event) {
  const drag = state.designer.drag;
  if (!drag) return;
  const box = drag.kind === 'block' ? getDesignerBlock(drag.key) : state.designer.config.fields[drag.key];
  const nextX = Math.max(0, Math.round(drag.originX + ((event.clientX - drag.startX) * drag.scaleX)));
  box.x = nextX;
  box.y = Math.max(0, Math.round(drag.originY + ((event.clientY - drag.startY) * drag.scaleY)));
  if (drag.kind === 'block' && drag.key === 'result') {
    moveResultRows(box.y);
    moveResultColumns(drag.resultOriginColumns, nextX - drag.originX);
  }
  const selector = drag.kind === 'block' ? `[data-block-key="${drag.key}"]` : `[data-field-key="${drag.key}"]`;
  const el = document.querySelector(selector);
  if (el) {
    el.style.left = `${percent(box.x, state.designer.template.page_width)}%`;
    el.style.top = `${percent(box.y, state.designer.template.page_height)}%`;
  }
}

function stopFieldDrag() {
  window.onpointermove = null;
  window.onpointerup = null;
  state.designer.drag = null;
  renderTemplateDesigner();
}

function handleTemplateKeydown(event) {
  if (!state.designer || !state.designer.selectedKey) return;
  const active = document.activeElement;
  if (active && ['INPUT', 'SELECT', 'TEXTAREA'].includes(active.tagName)) return;
  if (['Delete', 'Backspace'].includes(event.key)) {
    event.preventDefault();
    if (state.designer.selectedKind === 'block') deleteSelectedBlock();
    else deleteSelectedField();
    return;
  }
  if (!['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight'].includes(event.key)) return;
  event.preventDefault();
  const step = event.shiftKey ? 10 : 1;
  const target = state.designer.selectedKind === 'block'
    ? getDesignerBlock(state.designer.selectedKey)
    : state.designer.config.fields[state.designer.selectedKey];
  if (!target) return;
  if (event.key === 'ArrowLeft') target.x = Math.max(0, Number(target.x || 0) - step);
  if (event.key === 'ArrowRight') target.x = Number(target.x || 0) + step;
  if (event.key === 'ArrowUp') target.y = Math.max(0, Number(target.y || 0) - step);
  if (event.key === 'ArrowDown') target.y = Number(target.y || 0) + step;
  if (state.designer.selectedKind === 'block' && state.designer.selectedKey === 'result') {
    moveResultRows(target.y);
  }
  renderTemplateDesigner();
  focusTemplateCanvas();
}

function updateSelectedFieldFromProps() {
  const key = state.designer.selectedKey;
  if (!key) return;
  const box = state.designer.config.fields[key];
  box.x = Number(document.querySelector('[name="prop_x"]').value || 0);
  box.y = Number(document.querySelector('[name="prop_y"]').value || 0);
  box.w = Number(document.querySelector('[name="prop_w"]').value || 120);
  box.size = Number(document.querySelector('[name="prop_size"]').value || 12);
  box.align = document.querySelector('[name="prop_align"]').value || 'left';
  box.valign = document.querySelector('[name="prop_valign"]').value || 'top';
  renderTemplateDesigner();
}

function updateSelectedBlockFromProps() {
  const key = state.designer.selectedKey;
  const box = getDesignerBlock(key);
  if (!box) return;
  box.x = Number(document.querySelector('[name="block_x"]').value || 0);
  box.y = Number(document.querySelector('[name="block_y"]').value || 0);
  box.size = Number(document.querySelector('[name="block_size"]').value || 10);
  box.valign = document.querySelector('[name="block_valign"]').value || 'top';
  const rowH = Number(document.querySelector('[name="block_row_h"]').value || 22);
  if (key === 'result') {
    const cols = state.designer.config.result_columns || {};
    const oldX = Number((cols.home_t || cols.set || {}).x || box.x);
    const newX = Number(document.querySelector('[name="block_x"]').value || oldX);
    moveResultColumns(clone(cols), newX - oldX);
    moveResultRows(box.y, rowH);
  } else {
    box.row_h = rowH;
    box.number_w = Number(document.querySelector('[name="block_number_w"]').value || 32);
    box.name_w = Number(document.querySelector('[name="block_name_w"]').value || 124);
  }
  renderTemplateDesigner();
}

function deleteSelectedField() {
  const key = state.designer.selectedKey;
  if (!key) return;
  delete state.designer.config.fields[key];
  state.designer.selectedKey = null;
  renderTemplateDesigner();
}

function deleteSelectedBlock() {
  const key = state.designer.selectedKey;
  if (key === 'home_players') delete state.designer.config.home_players;
  if (key === 'away_players') delete state.designer.config.away_players;
  if (key === 'result') {
    delete state.designer.config.result_rows;
    delete state.designer.config.result_columns;
  }
  state.designer.selectedKey = null;
  state.designer.selectedKind = 'field';
  renderTemplateDesigner();
}

function getDesignerBlock(key) {
  if (key === 'home_players') return state.designer.config.home_players;
  if (key === 'away_players') return state.designer.config.away_players;
  if (key === 'result') return state.designer.config.result_columns?.home_t || state.designer.config.result_columns?.set;
  return null;
}

function resultRowHeight() {
  const rows = state.designer.config.result_rows || [];
  return rows.length > 1 ? Number(rows[1].y) - Number(rows[0].y) : 26;
}

function moveResultRows(startY, rowH = resultRowHeight()) {
  (state.designer.config.result_rows || []).forEach((row, index) => {
    row.y = Number(startY) + (index * Number(rowH));
  });
}

function moveResultColumns(originColumns, deltaX) {
  if (!originColumns) return;
  Object.entries(originColumns).forEach(([key, col]) => {
    if (!state.designer.config.result_columns[key]) return;
    state.designer.config.result_columns[key].x = Math.max(0, Math.round(Number(col.x || 0) + Number(deltaX || 0)));
  });
}

function clone(value) {
  return JSON.parse(JSON.stringify(value));
}

async function saveTemplateDesign() {
  const saved = await api(`/api/sheet-templates/${state.designer.template.id}`, {
    method: 'PUT',
    body: JSON.stringify({ config_json: state.designer.config })
  });
  const fresh = await api(`/api/sheet-templates/${state.designer.template.id}`);
  state.designer.template = fresh || saved;
  state.designer.config = safeJson(state.designer.template.config_json, state.designer.config);
  toast('Diseno de plantilla guardado');
  renderTemplateDesigner();
}

function focusTemplateCanvas() {
  setTimeout(() => $('#templateCanvas')?.focus?.(), 0);
}

function availableTemplateFields() {
  return [
    'tournament','date','time','court','home_team','away_team','home_sets','away_sets','winner',
    'referee_signature','home_captain','away_captain','home_coach','away_coach','observations',
    'home_players','away_players','result'
  ];
}

function fieldPreview(key) {
  const values = {
    tournament: 'Liga demo',
    date: '19/05/2026',
    time: '17:47',
    court: 'Cancha 1',
    home_team: 'Demo A',
    away_team: 'Demo B',
    home_sets: '3',
    away_sets: '1',
    winner: 'Demo A',
    referee_signature: 'Arbitro Demo',
    home_captain: 'Capitan A',
    away_captain: 'Capitan B',
    home_coach: 'DT A',
    away_coach: 'DT B',
    observations: 'Observaciones'
  };
  return values[key] || key;
}

function percent(value, total) {
  return (Number(value || 0) / Number(total || 1)) * 100;
}

function alignToJustify(align) {
  return align === 'center' ? 'center' : align === 'right' ? 'flex-end' : 'flex-start';
}

function verticalToAlign(align) {
  return align === 'middle' ? 'center' : align === 'bottom' ? 'flex-end' : 'flex-start';
}

function verticalToTransform(align) {
  return align === 'middle' ? 'translateY(-50%)' : align === 'bottom' ? 'translateY(-100%)' : 'none';
}

function safeJson(value, fallback) {
  try {
    return typeof value === 'string' ? JSON.parse(value) : value;
  } catch (_) {
    return fallback;
  }
}

function openTournamentModal() {
  openModal({
    eyebrow: 'Administracion',
    title: 'Crear torneo',
    subtitle: 'Define la categoria, subcategoria y formato competitivo.',
    body: `<form id="tournamentForm" class="space-y-4">
      ${field('Nombre', input('name','Nombre'))}
      ${field('Categoria', '<select class="input" name="category"><option>Masculino</option><option>Femenino</option><option>Mixto</option></select>')}
      ${field('Subcategoria', input('age_subcategory','Subcategoria'))}
      ${field('Formato', '<select class="input" name="format"><option>Liga</option><option>Eliminacion directa</option><option>Doble eliminacion</option><option>Grupos + playoffs</option></select>')}
      <button class="btn-primary w-full">Crear torneo</button>
    </form>`
  });
  $('#tournamentForm').onsubmit = submitForm('/api/tournaments', data => data, true);
}

function openTeamModal() {
  openModal({
    eyebrow: 'Administracion',
    title: 'Crear equipo',
    subtitle: 'El equipo se agrega al torneo activo y obtiene su link de ficha online.',
    body: `<form id="teamForm" class="space-y-4">
      ${field('Nombre', input('name','Nombre'))}
      ${field('DT / responsable', input('coach_name','DT / responsable'))}
      <button class="btn-primary w-full">Guardar equipo</button>
    </form>`
  });
  $('#teamForm').onsubmit = submitForm('/api/teams', data => ({ ...data, tournament_id: selectedTournamentId() }), true);
}

function openMatchModal(teams) {
  openModal({
    eyebrow: 'Fixture',
    title: 'Crear partido',
    subtitle: 'Programa un encuentro para el torneo seleccionado.',
    body: `<form id="matchForm" class="space-y-4">
      ${field('Local', `<select class="input" name="home_team_id">${teamOptions(teams)}</select>`)}
      ${field('Visitante', `<select class="input" name="away_team_id">${teamOptions(teams)}</select>`)}
      ${field('Fecha y hora', '<input class="input" name="scheduled_at" type="datetime-local">')}
      <button class="btn-primary w-full">Programar partido</button>
    </form>`
  });
  $('#matchForm').onsubmit = submitForm('/api/matches', data => ({ ...data, tournament_id: selectedTournamentId() }), true);
}

async function renderScore() {
  const id = selectedTournamentId();
  const matches = await api(`/api/matches?tournament_id=${id}&per_page=50`);
  const matchList = matches.data || [];
  const first = matchList.find(match => Number(match.id) === Number(state.scoreMatchId)) || matchList[0];
  if (!first) {
    view.innerHTML = '<section class="panel">No hay partidos programados.</section>';
    return;
  }
  state.scoreMatchId = first.id;
  const [score, advanced, homePlayers, awayPlayers, teamResponse] = await Promise.all([
    api(`/api/matches/${first.id}/score`),
    api(`/api/matches/${first.id}/advanced-score`),
    api(`/api/players?team_id=${first.home_team_id}&per_page=50`),
    api(`/api/players?team_id=${first.away_team_id}&per_page=50`),
    api(`/api/teams?tournament_id=${id}&per_page=100`)
  ]);
  connectWs(first.id);
  const currentSet = score.sets.find(s => !s.winner_team_id) || score.sets[score.sets.length - 1] || { set_number: 1, home_points: 0, away_points: 0, side_state: 'normal' };
  const players = [...(homePlayers.data || []), ...(awayPlayers.data || [])];
  const fullTeams = teamResponse.data || [];
  view.innerHTML = `
    <div class="grid gap-4">
      <section class="panel">
        <div class="flex flex-wrap items-center justify-between gap-3">
          <div><h1 class="text-2xl font-black">Modo arbitro/anotador</h1><p class="text-sm text-slate-500">${first.home_team} vs ${first.away_team}</p></div>
          <div class="flex flex-wrap items-center gap-2">
            <div class="segmented">
              <button type="button" data-score-mode="guided" class="${state.scoreMode === 'guided' ? 'active' : ''}">Guiado</button>
              <button type="button" data-score-mode="expert" class="${state.scoreMode === 'expert' ? 'active' : ''}">Experto</button>
            </div>
            <button id="exportScoresheetBtn" class="btn-muted" type="button">Planilla PDF</button>
            <select id="scoreMatch" class="input max-w-xs">${matchList.map(m => `<option value="${m.id}" ${m.id === first.id ? 'selected' : ''}>#${m.id} ${m.home_team} vs ${m.away_team}</option>`).join('')}</select>
          </div>
        </div>
      </section>
      ${state.scoreMode === 'guided' ? guidedScorePanel(first, currentSet, score, advanced) : expertScorePanel(first, currentSet, score)}
      <section class="grid gap-4 xl:grid-cols-2">
        <div class="panel"><h2 class="section-title">Rotaciones oficiales</h2>${table(['Set','Equipo','Pos','Jugador','Libero'], advanced.rotations.map(r => [r.set_number, r.team_name, r.position_number, r.player_name, Number(r.is_libero) ? 'Si' : 'No']))}</div>
        <div class="panel"><h2 class="section-title">Sustituciones y tiempos</h2>${table(['Tipo','Set','Equipo','Detalle'], [
          ...advanced.substitutions.map(s => ['Sustitucion', s.set_number, s.team_name, `${s.out_first} ${s.out_last} -> ${s.in_first} ${s.in_last}`]),
          ...advanced.timeouts.map(t => ['Tiempo', t.set_number, t.team_name, `${t.points_home}-${t.points_away}`])
        ])}</div>
        <div class="panel"><h2 class="section-title">Historial de rallys</h2>${table(['#','Set','Saque','Ganador','Resultado','Marcador'], advanced.rallies.map(r => [r.rally_number, r.set_number, r.serving_team || '-', r.winning_team || '-', r.result_type, `${r.points_home}-${r.points_away}`]))}</div>
        <div class="panel"><h2 class="section-title">Auditoria y firmas</h2>${table(['Tipo','Detalle','Fecha'], [
          ...advanced.signatures.map(s => ['Firma', `${s.signer_name} (${s.role}) ${String(s.signature_hash).slice(0, 10)}...`, fmt(s.signed_at)]),
          ...advanced.audit.map(a => ['Audit', `${a.action} - ${a.user_name || '-'}`, fmt(a.created_at)])
        ])}</div>
      </section>
      <section class="panel"><h2 class="section-title">Eventos en vivo</h2>${table(['Set','Equipo','Evento','Marcador','Hora'], score.events.map(e => [e.set_number, e.team_name || '-', e.event_type, `${e.points_home}-${e.points_away}`, fmt(e.created_at)]))}</section>
    </div>`;
  document.querySelectorAll('[data-team]').forEach(btn => btn.onclick = () => addEvent(first.id, btn.dataset.team, 'point'));
  document.querySelectorAll('[data-event]').forEach(btn => btn.onclick = () => addEvent(first.id, first.home_team_id, btn.dataset.event));
  document.querySelectorAll('[data-advanced]').forEach(btn => btn.onclick = () => openAdvancedScoreModal(btn.dataset.advanced, first, players, fullTeams));
  document.querySelectorAll('[data-score-mode]').forEach(btn => btn.onclick = () => {
    state.scoreMode = btn.dataset.scoreMode;
    localStorage.setItem('scoreMode', state.scoreMode);
    renderScore();
  });
  $('#scoreMatch').onchange = (event) => {
    state.scoreMatchId = Number(event.target.value);
    renderScore();
  };
  $('#exportScoresheetBtn').onclick = () => window.open(`/api/matches/${first.id}/scoresheet/ltv26`, '_blank');
}

function expertScorePanel(match, currentSet, score) {
  return `<section class="grid gap-3 md:grid-cols-3">
    <div class="panel text-center"><div class="section-title">Local</div><div class="text-4xl font-black">${currentSet.home_points}</div><div>${match.home_team}</div><button class="score-btn mt-4 w-full bg-court" data-team="${match.home_team_id}">+ Punto</button></div>
    <div class="panel text-center"><div class="section-title">Set ${currentSet.set_number} - Lado ${currentSet.side_state}</div><div class="text-3xl font-black">${score.sets_won.home} - ${score.sets_won.away}</div><div class="mt-3 grid grid-cols-2 gap-2">${eventButtons()}</div></div>
    <div class="panel text-center"><div class="section-title">Visitante</div><div class="text-4xl font-black">${currentSet.away_points}</div><div>${match.away_team}</div><button class="score-btn mt-4 w-full bg-zinc-900" data-team="${match.away_team_id}">+ Punto</button></div>
  </section>
  <section class="panel">
    <h2 class="section-title">Planilla reglamentaria</h2>
    <div class="grid gap-2 md:grid-cols-3 xl:grid-cols-6">
      <button class="btn-muted" data-advanced="rotation">Rotacion 1-6</button>
      <button class="btn-muted" data-advanced="libero">Libero</button>
      <button class="btn-muted" data-advanced="substitution">Sustitucion</button>
      <button class="btn-muted" data-advanced="timeout">Tiempo</button>
      <button class="btn-muted" data-advanced="rally">Rally</button>
      <button class="btn-muted" data-advanced="sanction">Sancion / tarjeta</button>
      <button class="btn-muted" data-advanced="signature">Firma arbitral</button>
    </div>
  </section>`;
}

function guidedScorePanel(match, currentSet, score, advanced) {
  const rotationCount = advanced.rotations.filter(row => Number(row.set_number) === Number(currentSet.set_number)).length;
  const hasSignature = advanced.signatures.length > 0;
  return `<section class="panel">
    <div class="flex flex-wrap items-center justify-between gap-3">
      <div>
        <h2 class="text-xl font-black">Carga guiada de planilla</h2>
        <p class="text-sm text-slate-500">Pensado para planilleros nuevos o cierre reglamentario.</p>
      </div>
      <div class="rounded border border-slate-200 bg-white px-4 py-2 text-sm font-black dark:border-zinc-800 dark:bg-zinc-900">Set ${currentSet.set_number} - ${currentSet.home_points}-${currentSet.away_points}</div>
    </div>
    <div class="stepper mt-4">
      ${guidedStep(1, 'Prepartido', `Confirmar equipos, libero y formacion inicial. Rotaciones cargadas: ${rotationCount}/12.`, [
        ['rotation', 'Cargar rotacion 1-6'],
        ['libero', 'Registrar libero']
      ])}
      ${guidedStep(2, 'Inicio y control de set', `Sets: ${score.sets_won.home}-${score.sets_won.away}. Lado de cancha: ${currentSet.side_state}.`, [
        ['rally', 'Registrar rally'],
        ['timeout', 'Tiempo solicitado'],
        ['substitution', 'Sustitucion']
      ])}
      ${guidedStep(3, 'Incidencias', 'Registrar sanciones, errores administrativos o correcciones con auditoria.', [
        ['sanction', 'Sancion / tarjeta'],
        ['rally', 'Rally corregido']
      ])}
      ${guidedStep(4, 'Cierre', hasSignature ? 'Planilla firmada digitalmente.' : 'Validar resultado y firmar al cierre.', [
        ['signature', 'Firma arbitral']
      ])}
    </div>
  </section>
  <section class="grid gap-3 md:grid-cols-2">
    <div class="panel text-center"><div class="section-title">Punto local</div><div class="text-3xl font-black">${match.home_team}</div><button class="score-btn mt-4 w-full bg-court" data-team="${match.home_team_id}">+ Punto</button></div>
    <div class="panel text-center"><div class="section-title">Punto visitante</div><div class="text-3xl font-black">${match.away_team}</div><button class="score-btn mt-4 w-full bg-zinc-900" data-team="${match.away_team_id}">+ Punto</button></div>
  </section>`;
}

function guidedStep(number, title, description, actions) {
  return `<div class="step-row">
    <span class="step-number">${number}</span>
    <div class="min-w-0">
      <div class="font-black">${title}</div>
      <p class="text-sm text-slate-500">${description}</p>
      <div class="step-actions">${actions.map(([type, label]) => `<button class="btn-muted text-sm" data-advanced="${type}">${label}</button>`).join('')}</div>
    </div>
  </div>`;
}

function openAdvancedScoreModal(type, match, players, fullTeams = []) {
  const homeFull = fullTeams.find(team => Number(team.id) === Number(match.home_team_id));
  const awayFull = fullTeams.find(team => Number(team.id) === Number(match.away_team_id));
  const teams = [
    { id: match.home_team_id, name: match.home_team, coach: homeFull?.coach_name || 'DT Local' },
    { id: match.away_team_id, name: match.away_team, coach: awayFull?.coach_name || 'DT Visitante' }
  ];
  const teamSelect = `<select class="input" name="team_id">${teams.map(team => `<option value="${team.id}">${team.name}</option>`).join('')}</select>`;
  const playerSelect = (name) => `<select class="input" name="${name}">${players.map(player => `<option value="${player.id}">${player.first_name} ${player.last_name} - ${player.team_name}</option>`).join('')}</select>`;
  const forms = {
    rotation: {
      title: 'Rotacion oficial',
      path: 'rotations',
      body: `${field('Equipo', teamSelect)}${field('Posicion 1 a 6', '<input class="input" name="position_number" type="number" min="1" max="6" value="1">')}${field('Jugador', playerSelect('player_id'))}${field('Libero', '<select class="input" name="is_libero"><option value="0">No</option><option value="1">Si</option></select>')}`
    },
    libero: {
      title: 'Gestion de libero',
      path: 'liberos',
      body: `${field('Equipo', teamSelect)}${field('Jugador libero', playerSelect('player_id'))}${field('Inicial', '<select class="input" name="is_starting"><option value="0">No</option><option value="1">Si</option></select>')}`
    },
    substitution: {
      title: 'Sustitucion reglamentaria',
      path: 'substitutions',
      body: `${field('Equipo', teamSelect)}${field('Sale', playerSelect('player_out_id'))}${field('Entra', playerSelect('player_in_id'))}${field('Motivo', input('reason','Motivo'))}`
    },
    timeout: {
      title: 'Tiempo solicitado',
      path: 'timeouts',
      body: `${field('Equipo', teamSelect)}${field('Solicitado por', input('requested_by','DT / capitan'))}`
    },
    rally: {
      title: 'Historial de rally',
      path: 'rallies',
      body: `${field('Equipo sacador', `<select class="input" name="serving_team_id">${teams.map(team => `<option value="${team.id}">${team.name}</option>`).join('')}</select>`)}${field('Equipo ganador', `<select class="input" name="winning_team_id">${teams.map(team => `<option value="${team.id}">${team.name}</option>`).join('')}</select>`)}${field('Resultado', '<select class="input" name="result_type"><option>point</option><option>error</option><option>ace</option><option>block</option><option>attack</option></select>')}${field('Notas', input('notes','Descripcion breve'))}`
    },
    signature: {
      title: 'Firma digital arbitral',
      path: 'signatures',
      body: `${field('Nombre del arbitro', input('signer_name','Nombre completo'))}${field('Rol', '<select class="input" name="role"><option value="principal">Principal</option><option value="segundo">Segundo</option><option value="anotador">Anotador</option></select>')}`
    },
    sanction: {
      title: 'Sancion / tarjeta',
      path: 'advanced-sanctions',
      body: `${field('Equipo', `<select class="input" name="team_id" id="sanctionTeamSelect">${teams.map(team => `<option value="${team.id}">${team.name}</option>`).join('')}</select>`)}${field('Plantel', `<select class="input" name="player_id" id="sanctionRosterSelect"></select>`)}
        <div class="field"><span class="field-label">Tarjeta</span><div class="card-choice">
          <input id="cardYellow" type="radio" name="card_type" value="yellow" checked><label for="cardYellow"><span class="card-swatch yellow"></span>Tarjeta amarilla</label>
          <input id="cardRed" type="radio" name="card_type" value="red"><label for="cardRed"><span class="card-swatch red"></span>Tarjeta roja</label>
        </div></div>
        ${field('Motivo', input('reason','Motivo'))}`
    }
  };
  const config = forms[type];
  openModal({
    eyebrow: 'Planilla electronica',
    title: config.title,
    subtitle: 'El registro queda en eventos, historial y auditoria arbitral.',
    body: `<form id="advancedScoreForm" class="space-y-4">${config.body}<button class="btn-primary w-full">Registrar</button></form>`
  });
  if (type === 'sanction') bindSanctionRoster(teams, players);
  $('#advancedScoreForm').onsubmit = async (event) => {
    event.preventDefault();
    const payload = Object.fromEntries(new FormData(event.target));
    if (payload.player_id === 'coach' || payload.player_id === '') {
      payload.reason = `${payload.player_id === 'coach' ? 'DT - ' : ''}${payload.reason || ''}`.trim();
      payload.player_id = null;
    }
    await api(`/api/matches/${match.id}/${config.path}`, { method: 'POST', body: JSON.stringify(payload) });
    closeModal();
    toast('Registro guardado');
    renderScore();
  };
}

function bindSanctionRoster(teams, players) {
  const teamSelectEl = $('#sanctionTeamSelect');
  const rosterSelect = $('#sanctionRosterSelect');
  const refresh = () => {
    const teamId = Number(teamSelectEl.value);
    const team = teams.find(item => Number(item.id) === teamId);
    const roster = players.filter(player => Number(player.team_id) === teamId);
    rosterSelect.innerHTML = [
      '<option value="">Equipo / banco</option>',
      `<option value="coach">DT - ${team?.coach || 'Responsable'}</option>`,
      ...roster.map(player => `<option value="${player.id}">#${player.jersey_number || '-'} ${player.first_name} ${player.last_name}</option>`)
    ].join('');
  };
  teamSelectEl.onchange = refresh;
  refresh();
}

function eventButtons() {
  return ['serve','ace','block','attack','error','rotation'].map(e => `<button class="btn-muted text-xs" data-event="${e}">${e}</button>`).join('');
}

async function addEvent(matchId, teamId, eventType) {
  try {
    await api(`/api/matches/${matchId}/events`, { method: 'POST', body: JSON.stringify({ team_id: Number(teamId), event_type: eventType }) });
    toast('Evento registrado');
    renderScore();
  } catch (err) {
    toast(err.message, 'error');
  }
}

function connectWs(matchId) {
  if (state.ws) return;
  try {
    state.ws = new WebSocket(`ws://${location.hostname}:8081?match_id=${matchId}`);
    state.ws.onmessage = () => state.route === 'score' && renderScore();
  } catch (_) {}
}

function renderTeamLink() {
  view.innerHTML = `<section class="panel max-w-xl">
    <h1 class="text-2xl font-black">Ficha online de jugador</h1>
    <p class="mb-4 text-sm text-slate-500">Usa el token del equipo. Ejemplo seed: 11111111111111111111111111111111</p>
    <form id="linkForm" class="space-y-2">
      ${input('token','Token del equipo')}${input('first_name','Nombre')}${input('last_name','Apellido')}${input('document_id','DNI')}${input('birth_date','Nacimiento', 'date')}${input('jersey_number','Numero', 'number')}
      <select class="input" name="position"><option>Punta</option><option>Armador</option><option>Central</option><option>Opuesto</option><option>Libero</option><option>Universal</option></select>
      <button class="btn-primary w-full">Enviar ficha</button>
    </form>
  </section>`;
  $('#linkForm').onsubmit = async (e) => {
    e.preventDefault();
    const data = Object.fromEntries(new FormData(e.target));
    const token = data.token;
    delete data.token;
    await api(`/api/team-links/${token}/players`, { method: 'POST', body: JSON.stringify(data) });
    toast('Ficha registrada');
    e.target.reset();
  };
}

function submitForm(path, mapper, shouldCloseModal = false, method = 'POST') {
  return async (e) => {
    e.preventDefault();
    const payload = mapper(Object.fromEntries(new FormData(e.target)));
    await api(path, { method, body: JSON.stringify(payload) });
    toast('Guardado correctamente');
    if (shouldCloseModal) closeModal();
    await loadBase();
    render();
  };
}

function userTable(users) {
  const rows = users.map(user => [
    `<div class="font-bold">${user.name}</div><div class="text-xs text-slate-500">${user.email}</div>`,
    user.role,
    Number(user.active) ? 'Activo' : 'Inactivo',
    `<div class="flex gap-2">
      <button class="btn-muted text-xs" data-edit-user="${user.id}">Editar</button>
      <button class="btn-muted text-xs" data-toggle-user="${user.id}" data-active="${Number(user.active)}">${Number(user.active) ? 'Desactivar' : 'Activar'}</button>
    </div>`
  ]);
  return table(['Usuario','Rol','Estado','Acciones'], rows);
}

function templateTable(templates) {
  const rows = templates.map(template => [
    `<div class="font-bold">${template.name}</div><div class="text-xs text-slate-500">${template.code}</div>`,
    `${template.page_width}x${template.page_height}`,
    template.image_path,
    Number(template.assigned_to_tournament) ? 'Asignada' : (Number(template.active) ? 'Activa' : 'Inactiva'),
    `<div class="flex flex-wrap gap-2"><button class="btn-muted text-xs" type="button" data-edit-template="${template.id}">Editar diseno</button><button class="btn-muted text-xs" type="button" data-assign-template="${template.id}">${Number(template.assigned_to_tournament) ? 'Reasignar' : 'Asignar al torneo'}</button></div>`
  ]);
  return table(['Plantilla','Tamano','Imagen','Estado','Acciones'], rows);
}

function table(headers, rows) {
  return `<div class="overflow-x-auto"><table class="table"><thead><tr>${headers.map(h => `<th>${h}</th>`).join('')}</tr></thead><tbody>${(rows.length ? rows : [['Sin datos']]).map(r => `<tr>${r.map(c => `<td>${c ?? '-'}</td>`).join('')}</tr>`).join('')}</tbody></table></div>`;
}

function input(name, placeholder, type = 'text', value = '') {
  return `<input class="input" name="${name}" type="${type}" value="${escapeAttr(value)}" placeholder="${placeholder}">`;
}

function field(label, control) {
  return `<label class="field"><span class="field-label">${label}</span>${control}</label>`;
}

function escapeAttr(value) {
  return String(value ?? '').replaceAll('&', '&amp;').replaceAll('"', '&quot;').replaceAll('<', '&lt;').replaceAll('>', '&gt;');
}

function escapeHtml(value) {
  return String(value ?? '').replaceAll('&', '&amp;').replaceAll('<', '&lt;').replaceAll('>', '&gt;');
}

function teamOptions(teams = []) {
  return teams.map(t => `<option value="${t.id}">${t.name}</option>`).join('');
}

function metricCard(label, value) {
  return `<div class="min-w-[112px] rounded border border-slate-200 bg-slate-50 px-4 py-3 text-right dark:border-zinc-800 dark:bg-zinc-900">
    <div class="text-2xl font-black">${value}</div>
    <div class="text-xs font-bold uppercase text-slate-500">${label}</div>
  </div>`;
}

function fmt(value) {
  if (!value) return '-';
  return new Date(value.replace(' ', 'T')).toLocaleString('es-AR', { dateStyle: 'short', timeStyle: 'short' });
}

$('#loginBtn').onclick = openLoginModal;
$('#sideLoginBtn').onclick = openLoginModal;
$('#sidebarToggle').onclick = toggleSidebar;
$('#mobileMenuBtn').onclick = openMobileMenu;
$('#mobileMenuBackdrop').onclick = closeMobileMenu;
$('#themeBtn').onclick = () => document.documentElement.classList.toggle('dark');
$('#refreshBtn').onclick = () => render().catch(err => toast(err.message, 'error'));
$('#fixtureBtn').onclick = async () => {
  try {
    await api(`/api/tournaments/${selectedTournamentId()}/fixture`, { method: 'POST', body: JSON.stringify({}) });
    toast('Fixture generado');
    await render();
  } catch (err) {
    if (err.status === 401) {
      clearSession();
      updateSession();
      openLoginModal();
    }
    toast(err.message, 'error');
  }
};
document.querySelectorAll('[data-modal-close]').forEach(el => el.onclick = closeModal);
document.addEventListener('keydown', (event) => {
  if (event.key === 'Escape') closeModal();
});
document.querySelectorAll('[data-route]').forEach(btn => btn.onclick = () => setRoute(btn.dataset.route));
$('#tournamentSelect').onchange = () => render().catch(err => toast(err.message, 'error'));

applySidebarState();
validateStoredSession()
  .then(loadBase)
  .then(render)
  .catch(err => toast(err.message, 'error'));
