const { useState, useEffect, useRef } = React;

// ==========================================
// CONFIGURACIÓN DE ENTORNO
// ==========================================
const USE_LOCAL_STORAGE = false;
const API_URL = 'api.php';

const DAYS_OF_WEEK = ["Domingo", "Lunes", "Martes", "Miércoles", "Jueves", "Viernes", "Sábado"];

/** Fecha local YYYY-MM-DD (completados por día calendario, no por weekday) */
const todayISO = () => {
  const d = new Date();
  const y = d.getFullYear();
  const m = String(d.getMonth() + 1).padStart(2, '0');
  const day = String(d.getDate()).padStart(2, '0');
  return `${y}-${m}-${day}`;
};

// ==========================================
// CATÁLOGO DE EJERCICIOS POR PARTE DEL CUERPO
// video_url queda listo para el futuro (null por ahora)
// ==========================================
const BODY_PARTS = [
  { id: 'chest', name: 'Pecho' },
  { id: 'back', name: 'Espalda' },
  { id: 'shoulders', name: 'Hombros' },
  { id: 'arms', name: 'Brazos' },
  { id: 'legs', name: 'Piernas' },
  { id: 'glutes', name: 'Glúteos' },
  { id: 'core', name: 'Core / Abs' },
  { id: 'cardio', name: 'Cardio' },
];

const EXERCISE_CATALOG = [
  // Pecho
  { id: 'pushups', bodyPart: 'chest', name: 'Flexiones', description: 'Manos al ancho de hombros, cuerpo en línea recta. Baja el pecho hacia el suelo y empuja hacia arriba.', is_time: false, defaultSets: 3, defaultReps: 12, video_url: null },
  { id: 'pushups-wide', bodyPart: 'chest', name: 'Flexiones abiertas', description: 'Manos más abiertas que los hombros. Enfatiza pecho exterior. Baja controlando y sube.', is_time: false, defaultSets: 3, defaultReps: 10, video_url: null },
  { id: 'pushups-diamond', bodyPart: 'chest', name: 'Flexiones diamante', description: 'Manos juntas formando un diamante bajo el pecho. Enfatiza tríceps y pecho interno.', is_time: false, defaultSets: 3, defaultReps: 8, video_url: null },
  { id: 'dips-chair', bodyPart: 'chest', name: 'Fondos en silla', description: 'Manos en el borde de una silla, baja flexionando codos y sube empujando.', is_time: false, defaultSets: 3, defaultReps: 12, video_url: null },

  // Espalda
  { id: 'superman', bodyPart: 'back', name: 'Superman', description: 'Boca abajo, eleva brazos y piernas a la vez. Mantén y baja con control.', is_time: false, defaultSets: 3, defaultReps: 12, video_url: null },
  { id: 'bird-dog', bodyPart: 'back', name: 'Bird dog', description: 'A cuatro patas, extiende brazo y pierna opuestos. Mantén el tronco estable.', is_time: false, defaultSets: 3, defaultReps: 10, video_url: null },
  { id: 'reverse-snow', bodyPart: 'back', name: 'Ángel inverso', description: 'Boca abajo, brazos a los lados. Desliza los brazos hacia arriba como un ángel en la nieve.', is_time: false, defaultSets: 3, defaultReps: 12, video_url: null },
  { id: 'door-row', bodyPart: 'back', name: 'Remo en puerta', description: 'Sujeta el marco de la puerta e inclínate hacia atrás. Tira del pecho hacia las manos.', is_time: false, defaultSets: 3, defaultReps: 12, video_url: null },

  // Hombros
  { id: 'pike-pushups', bodyPart: 'shoulders', name: 'Flexiones pike', description: 'Cadera arriba (forma de V). Baja la cabeza hacia el suelo flexionando codos.', is_time: false, defaultSets: 3, defaultReps: 8, video_url: null },
  { id: 'arm-circles', bodyPart: 'shoulders', name: 'Círculos de brazos', description: 'Brazos extendidos a los lados. Haz círculos amplios hacia adelante y atrás.', is_time: true, defaultSets: 3, defaultReps: 30, video_url: null },
  { id: 'shoulder-taps', bodyPart: 'shoulders', name: 'Toques de hombro', description: 'En posición de plancha, toca un hombro con la mano opuesta sin balancear la cadera.', is_time: false, defaultSets: 3, defaultReps: 16, video_url: null },

  // Brazos
  { id: 'triceps-dips', bodyPart: 'arms', name: 'Fondos de tríceps', description: 'Manos en silla detrás de ti. Baja flexionando codos y sube sin bloquearlos.', is_time: false, defaultSets: 3, defaultReps: 12, video_url: null },
  { id: 'diamond-close', bodyPart: 'arms', name: 'Flexiones cerradas', description: 'Manos juntas bajo el pecho. Baja y sube enfatizando tríceps.', is_time: false, defaultSets: 3, defaultReps: 10, video_url: null },
  { id: 'bicep-iso', bodyPart: 'arms', name: 'Curl isométrico', description: 'Con una bandera/toalla o peso improvisado, mantén la flexión de codo a 90°.', is_time: true, defaultSets: 3, defaultReps: 25, video_url: null },

  // Piernas
  { id: 'squats', bodyPart: 'legs', name: 'Sentadillas', description: 'Pies al ancho de hombros. Baja como si te sentaras, rodillas alineadas con los pies.', is_time: false, defaultSets: 3, defaultReps: 15, video_url: null },
  { id: 'lunges', bodyPart: 'legs', name: 'Zancadas', description: 'Da un paso largo hacia adelante y baja la rodilla trasera. Alterna piernas.', is_time: false, defaultSets: 3, defaultReps: 12, video_url: null },
  { id: 'bulgarian', bodyPart: 'legs', name: 'Sentadilla búlgara', description: 'Pie trasero apoyado en silla. Baja con la pierna delantera controlando el movimiento.', is_time: false, defaultSets: 3, defaultReps: 10, video_url: null },
  { id: 'calf-raises', bodyPart: 'legs', name: 'Elevaciones de gemelos', description: 'De pie, sube sobre las puntas de los pies y baja lento.', is_time: false, defaultSets: 3, defaultReps: 20, video_url: null },
  { id: 'wall-sit', bodyPart: 'legs', name: 'Sentadilla en pared', description: 'Espalda contra la pared, muslos paralelos al suelo. Mantén la posición.', is_time: true, defaultSets: 3, defaultReps: 40, video_url: null },

  // Glúteos
  { id: 'glute-bridge', bodyPart: 'glutes', name: 'Puente de glúteos', description: 'Boca arriba, pies en el suelo. Eleva la cadera apretando glúteos y baja.', is_time: false, defaultSets: 3, defaultReps: 15, video_url: null },
  { id: 'hip-thrust', bodyPart: 'glutes', name: 'Hip thrust', description: 'Espalda apoyada en un sofá/banco. Empuja la cadera hacia arriba y baja controlando.', is_time: false, defaultSets: 3, defaultReps: 12, video_url: null },
  { id: 'donkey-kick', bodyPart: 'glutes', name: 'Patada de glúteo', description: 'A cuatro patas, eleva una pierna hacia atrás manteniendo la rodilla flexionada.', is_time: false, defaultSets: 3, defaultReps: 12, video_url: null },
  { id: 'fire-hydrant', bodyPart: 'glutes', name: 'Hidrante', description: 'A cuatro patas, abre la rodilla hacia el lado sin girar la cadera.', is_time: false, defaultSets: 3, defaultReps: 12, video_url: null },

  // Core
  { id: 'plank', bodyPart: 'core', name: 'Plancha', description: 'Antebrazos en el suelo, cuerpo en línea. Aprieta abdomen y glúteos. No dejes caer la cadera.', is_time: true, defaultSets: 3, defaultReps: 30, video_url: null },
  { id: 'crunches', bodyPart: 'core', name: 'Crunches', description: 'Boca arriba, eleva hombros del suelo contrayendo el abdomen. Baja lento.', is_time: false, defaultSets: 3, defaultReps: 20, video_url: null },
  { id: 'leg-raises', bodyPart: 'core', name: 'Elevaciones de piernas', description: 'Boca arriba, sube las piernas rectas y bájalas sin tocar el suelo.', is_time: false, defaultSets: 3, defaultReps: 12, video_url: null },
  { id: 'russian-twist', bodyPart: 'core', name: 'Giro ruso', description: 'Sentado, tronco inclinado. Gira el torso de lado a lado tocando el suelo.', is_time: false, defaultSets: 3, defaultReps: 20, video_url: null },
  { id: 'dead-bug', bodyPart: 'core', name: 'Dead bug', description: 'Boca arriba, brazo y pierna opuestos se extienden. Mantén la espalda pegada al suelo.', is_time: false, defaultSets: 3, defaultReps: 10, video_url: null },
  { id: 'side-plank', bodyPart: 'core', name: 'Plancha lateral', description: 'Apoyado en un antebrazo, cuerpo en línea de lado. Mantén cadera elevada.', is_time: true, defaultSets: 3, defaultReps: 25, video_url: null },

  // Cardio
  { id: 'jumping-jacks', bodyPart: 'cardio', name: 'Jumping jacks', description: 'Salta abriendo piernas y elevando brazos, luego vuelve a posición inicial.', is_time: false, defaultSets: 3, defaultReps: 30, video_url: null },
  { id: 'high-knees', bodyPart: 'cardio', name: 'Rodillas altas', description: 'Corre en el sitio elevando las rodillas lo más alto posible.', is_time: true, defaultSets: 3, defaultReps: 30, video_url: null },
  { id: 'burpees', bodyPart: 'cardio', name: 'Burpees', description: 'Agáchate, plancha, flexión opcional, salta arriba con brazos. Ritmo constante.', is_time: false, defaultSets: 3, defaultReps: 8, video_url: null },
  { id: 'mountain-climbers', bodyPart: 'cardio', name: 'Mountain climbers', description: 'En plancha, lleva rodillas al pecho alternando rápido.', is_time: true, defaultSets: 3, defaultReps: 30, video_url: null },
  { id: 'walk', bodyPart: 'cardio', name: 'Caminar', description: 'Camina a ritmo moderado. Mantén postura erguida y respiración constante.', is_time: true, defaultSets: 1, defaultReps: 300, video_url: null },
];

const emptyExercise = () => ({
  name: '', description: '', video_url: null, sets: 3, reps: 10, is_time: false, rest_secs: 60
});

const exerciseFromCatalog = (item) => ({
  name: item.name,
  description: item.description || '',
  video_url: item.video_url || null,
  sets: item.defaultSets || 3,
  reps: item.defaultReps || 10,
  is_time: !!item.is_time,
  rest_secs: 60,
});

// ==========================================
// SONIDOS — motor en sounds.js (HTMLAudio + WAV)
// ==========================================
const DEFAULT_SOUND_ENABLED = true;
const SoundFx = window.SoundFx || {
  applyFromUser: () => ({ sound_enabled: true }),
  setEnabled() {},
  isEnabled: () => false,
  unlock() {},
  rep() {},
  start() {},
  tick() {},
  setDone() {},
  restDone() {},
  workoutDone() {},
  preview() {},
};

const normalizeSoundPrefs = (user) => SoundFx.applyFromUser(user);

// ==========================================
// ICONOS (SVG, estilo línea)
// ==========================================
const Icon = ({ path, size = "icon", fill = false }) => (
  <svg className={size} viewBox="0 0 24 24" fill={fill ? "currentColor" : "none"}
       stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
    {path}
  </svg>
);

const Icons = {
  dumbbell: <Icon path={<><path d="m6.5 6.5 11 11"/><path d="m21 21-1-1"/><path d="m3 3 1 1"/><path d="m18 22 4-4"/><path d="m2 6 4-4"/><path d="m3 10 7-7"/><path d="m14 21 7-7"/></>} />,
  settings: <Icon path={<><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 1 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 1 1-2.83-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 1 1 2.83-2.83l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 1 1 2.83 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z"/></>} />,
  play: <Icon path={<polygon points="6 3 20 12 6 21 6 3"/>} fill={true} />,
  moon: <Icon path={<path d="M12 3a6 6 0 0 0 9 9 9 9 0 1 1-9-9Z"/>} />,
  sun: <Icon path={<><circle cx="12" cy="12" r="4"/><path d="M12 2v2"/><path d="M12 20v2"/><path d="m4.93 4.93 1.41 1.41"/><path d="m17.66 17.66 1.41 1.41"/><path d="M2 12h2"/><path d="M20 12h2"/><path d="m6.34 17.66-1.41 1.41"/><path d="m19.07 4.93-1.41 1.41"/></>} />,
  plus: <Icon path={<><path d="M5 12h14"/><path d="M12 5v14"/></>} size="icon-sm" />,
  trash: <Icon path={<><path d="M3 6h18"/><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/></>} size="icon-sm" />,
  close: <Icon path={<><path d="M18 6 6 18"/><path d="m6 6 12 12"/></>} size="icon-sm" />,
  repeat: <Icon path={<><path d="m17 2 4 4-4 4"/><path d="M3 11v-1a4 4 0 0 1 4-4h14"/><path d="m7 22-4-4 4-4"/><path d="M21 13v1a4 4 0 0 1-4 4H3"/></>} size="icon-sm" />,
  clock: <Icon path={<><circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/></>} size="icon-sm" />,
  info: <Icon path={<><circle cx="12" cy="12" r="10"/><path d="M12 16v-4"/><path d="M12 8h.01"/></>} size="icon-sm" />,
  help: <Icon path={<><circle cx="12" cy="12" r="10"/><path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3"/><path d="M12 17h.01"/></>} size="icon-sm" />,
  back: <Icon path={<><path d="m15 18-6-6 6-6"/></>} size="icon-sm" />,
  catalog: <Icon path={<><path d="M4 19.5A2.5 2.5 0 0 1 6.5 17H20"/><path d="M6.5 2H20v20H6.5A2.5 2.5 0 0 1 4 19.5v-15A2.5 2.5 0 0 1 6.5 2z"/></>} size="icon-sm" />,
  routines: <Icon path={<><path d="M8 6h13"/><path d="M8 12h13"/><path d="M8 18h13"/><path d="M3 6h.01"/><path d="M3 12h.01"/><path d="M3 18h.01"/></>} />,
  logout: <Icon path={<><path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"/><polyline points="16 17 21 12 16 7"/><line x1="21" y1="12" x2="9" y2="12"/></>} size="icon-sm" />,
  user: <Icon path={<><path d="M19 21v-2a4 4 0 0 0-4-4H9a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/></>} size="icon-sm" />,
  check: <Icon path={<><path d="M20 6 9 17l-5-5"/></>} size="icon-sm" />,
  volume: <Icon path={<><polygon points="11 5 6 9 2 9 2 15 6 15 11 19 11 5"/><path d="M15.54 8.46a5 5 0 0 1 0 7.07"/><path d="M19.07 4.93a10 10 0 0 1 0 14.14"/></>} />,
  volumeOff: <Icon path={<><polygon points="11 5 6 9 2 9 2 15 6 15 11 19 11 5"/><line x1="23" y1="9" x2="17" y2="15"/><line x1="17" y1="9" x2="23" y2="15"/></>} />,
  edit: <Icon path={<><path d="M12 20h9"/><path d="M16.5 3.5a2.12 2.12 0 0 1 3 3L7 19l-4 1 1-4Z"/></>} size="icon-sm" />,
  download: <Icon path={<><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></>} size="icon-sm" />,
  share: <Icon path={<><path d="M4 12v8a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-8"/><polyline points="16 6 12 2 8 6"/><line x1="12" y1="2" x2="12" y2="15"/></>} size="icon-sm" />,
  message: <Icon path={<><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/></>} size="icon-sm" />,
};

/** Logo RutinaSimple (SVG/CSS) */
function AppLogo({ size = 72, className = '' }) {
  return (
    <div className={`app-logo ${className}`} style={{ width: size, height: size }} aria-hidden="true">
      <svg viewBox="0 0 100 100" fill="none" xmlns="http://www.w3.org/2000/svg">
        <circle className="app-logo-ring" cx="50" cy="50" r="34" strokeWidth="7" />
        <path className="app-logo-arc" d="M50 16 A34 34 0 0 1 84 50" strokeWidth="7" strokeLinecap="round" />
        <path
          className="app-logo-mark"
          d="M32 58 L44 46 L52 54 L70 36"
          strokeWidth="7"
          strokeLinecap="round"
          strokeLinejoin="round"
        />
      </svg>
    </div>
  );
}

const DEFAULT_ROUTINES = [
  {
    id: 1, name: "Pecho y Tríceps", day_of_week: 1,
    exercises: [
      { id: 101, name: "Flexiones", description: "Manos al ancho de los hombros, cuerpo recto. Baja controlando el pecho.", video_url: null, sets: 3, reps: 15, is_time: false, rest_secs: 30 },
      { id: 102, name: "Fondos en silla", description: "Apoya las manos en el borde de la silla y baja el cuerpo flexionando los codos.", video_url: null, sets: 3, reps: 12, is_time: false, rest_secs: 45 }
    ]
  },
  {
    id: 2, name: "Core Intenso", day_of_week: 1,
    exercises: [
      { id: 103, name: "Plancha", description: "Antebrazos en el suelo, cuerpo en línea recta. Aprieta el abdomen.", video_url: null, sets: 3, reps: 30, is_time: true, rest_secs: 60 }
    ]
  }
];

// ==========================================
// AUTH + SERVICIO DE DATOS
// ==========================================
const AUTH_TOKEN_KEY = 'rutinasimple_token';
const AUTH_USER_KEY = 'rutinasimple_user';
const LOCAL_USERS_KEY = 'rutinasimple_users';

const apiFetch = async (action, options = {}) => {
  const token = localStorage.getItem(AUTH_TOKEN_KEY);
  const headers = {
    'Content-Type': 'application/json',
    ...(options.headers || {}),
  };
  if (token) headers['X-Auth-Token'] = token;

  const res = await fetch(`${API_URL}?action=${action}`, {
    ...options,
    headers,
  });
  const data = await res.json().catch(() => ({ error: 'Respuesta inválida del servidor' }));
  if (!res.ok || data.error) {
    throw new Error(data.error || 'Error de servidor');
  }
  return data;
};

const AuthService = {
  getStoredUser() {
    try {
      const raw = localStorage.getItem(AUTH_USER_KEY);
      return raw ? JSON.parse(raw) : null;
    } catch {
      return null;
    }
  },

  getToken() {
    return localStorage.getItem(AUTH_TOKEN_KEY);
  },

  persistSession(token, user) {
    localStorage.setItem(AUTH_TOKEN_KEY, token);
    localStorage.setItem(AUTH_USER_KEY, JSON.stringify(user));
  },

  clearSession() {
    localStorage.removeItem(AUTH_TOKEN_KEY);
    localStorage.removeItem(AUTH_USER_KEY);
  },

  async register({ name, email, password }) {
    if (USE_LOCAL_STORAGE) {
      const users = JSON.parse(localStorage.getItem(LOCAL_USERS_KEY) || '[]');
      const normalized = email.trim().toLowerCase();
      if (users.some(u => u.email === normalized)) {
        throw new Error('Ya existe una cuenta con ese email');
      }
      if (password.length < 6) {
        throw new Error('La contraseña debe tener al menos 6 caracteres');
      }
      const user = {
        id: Date.now(),
        name: name.trim(),
        email: normalized,
        avatar: null,
        sound_enabled: DEFAULT_SOUND_ENABLED,
      };
      users.push({ ...user, password });
      localStorage.setItem(LOCAL_USERS_KEY, JSON.stringify(users));
      const token = 'local_' + user.id;
      this.persistSession(token, user);
      SoundFx.applyFromUser(user);
      // Rutinas de ejemplo solo para la primera cuenta local
      const key = `rutinasimple_routines_${user.id}`;
      if (!localStorage.getItem(key)) {
        localStorage.setItem(key, JSON.stringify(DEFAULT_ROUTINES));
      }
      return { token, user };
    }

    const data = await apiFetch('register', {
      method: 'POST',
      body: JSON.stringify({ name, email, password }),
    });
    this.persistSession(data.token, data.user);
    SoundFx.applyFromUser(data.user);
    return data;
  },

  async login({ email, password }) {
    if (USE_LOCAL_STORAGE) {
      const users = JSON.parse(localStorage.getItem(LOCAL_USERS_KEY) || '[]');
      const normalized = email.trim().toLowerCase();
      const found = users.find(u => u.email === normalized && u.password === password);
      if (!found) throw new Error('Email o contraseña incorrectos');
      const prefs = normalizeSoundPrefs(found);
      const user = {
        id: found.id,
        name: found.name,
        email: found.email,
        avatar: found.avatar || null,
        ...prefs,
      };
      const token = 'local_' + user.id;
      this.persistSession(token, user);
      SoundFx.applyFromUser(user);
      return { token, user };
    }

    const data = await apiFetch('login', {
      method: 'POST',
      body: JSON.stringify({ email, password }),
    });
    this.persistSession(data.token, data.user);
    SoundFx.applyFromUser(data.user);
    return data;
  },

  async logout() {
    if (!USE_LOCAL_STORAGE) {
      try { await apiFetch('logout', { method: 'POST', body: '{}' }); } catch (_) {}
    }
    this.clearSession();
  },

  async me() {
    const stored = this.getStoredUser();
    const token = this.getToken();
    if (!token || !stored) return null;

    if (USE_LOCAL_STORAGE) {
      const prefs = normalizeSoundPrefs(stored);
      const user = { ...stored, ...prefs };
      SoundFx.applyFromUser(user);
      return user;
    }

    try {
      const data = await apiFetch('me');
      this.persistSession(token, data.user);
      SoundFx.applyFromUser(data.user);
      return data.user;
    } catch {
      this.clearSession();
      return null;
    }
  },

  async updateProfile(patch = {}) {
    const payload = {};

    if (Object.prototype.hasOwnProperty.call(patch, 'name')) {
      const trimmed = (patch.name || '').trim();
      if (!trimmed) throw new Error('El nombre no puede estar vacío');
      payload.name = trimmed;
    }

    if (!Object.keys(payload).length) {
      throw new Error('Nada que actualizar');
    }

    if (USE_LOCAL_STORAGE) {
      const stored = this.getStoredUser();
      const token = this.getToken();
      if (!stored || !token) throw new Error('No autenticado');
      const users = JSON.parse(localStorage.getItem(LOCAL_USERS_KEY) || '[]');
      const idx = users.findIndex(u => u.id === stored.id);
      const next = { ...stored, ...payload };
      if (idx > -1) {
        users[idx] = { ...users[idx], ...payload };
        localStorage.setItem(LOCAL_USERS_KEY, JSON.stringify(users));
      }
      const user = { ...next, ...normalizeSoundPrefs(next) };
      this.persistSession(token, user);
      SoundFx.applyFromUser(user);
      return { user };
    }

    const data = await apiFetch('update_profile', {
      method: 'POST',
      body: JSON.stringify(payload),
    });
    const token = this.getToken();
    this.persistSession(token, data.user);
    SoundFx.applyFromUser(data.user);
    return data;
  },

  async updateSoundPrefs({ sound_enabled }) {
    const payload = { sound_enabled: !!sound_enabled };

    if (USE_LOCAL_STORAGE) {
      const stored = this.getStoredUser();
      const token = this.getToken();
      if (!stored || !token) throw new Error('No autenticado');
      const users = JSON.parse(localStorage.getItem(LOCAL_USERS_KEY) || '[]');
      const idx = users.findIndex(u => u.id === stored.id);
      const next = { ...stored, ...payload };
      if (idx > -1) {
        users[idx] = { ...users[idx], ...payload };
        localStorage.setItem(LOCAL_USERS_KEY, JSON.stringify(users));
      }
      const user = { ...next, ...normalizeSoundPrefs(next) };
      this.persistSession(token, user);
      SoundFx.applyFromUser(user);
      return { user };
    }

    const data = await apiFetch('update_sound_prefs', {
      method: 'POST',
      body: JSON.stringify(payload),
    });
    const token = this.getToken();
    this.persistSession(token, data.user);
    SoundFx.applyFromUser(data.user);
    return data;
  },

  async sendFeedback({ category, message, contact_email }) {
    if (USE_LOCAL_STORAGE) {
      throw new Error('El feedback requiere conexión con el servidor');
    }
    return apiFetch('send_feedback', {
      method: 'POST',
      body: JSON.stringify({ category, message, contact_email }),
    });
  },

  async uploadAvatar(file) {
    if (USE_LOCAL_STORAGE) {
      const stored = this.getStoredUser();
      const token = this.getToken();
      if (!stored || !token) throw new Error('No autenticado');
      const dataUrl = await readFileAsDataURL(file);
      const users = JSON.parse(localStorage.getItem(LOCAL_USERS_KEY) || '[]');
      const idx = users.findIndex(u => u.id === stored.id);
      if (idx > -1) {
        users[idx] = { ...users[idx], avatar: dataUrl };
        localStorage.setItem(LOCAL_USERS_KEY, JSON.stringify(users));
      }
      const user = { ...stored, avatar: dataUrl };
      this.persistSession(token, user);
      return { user };
    }

    const form = new FormData();
    form.append('avatar', file);
    const token = this.getToken();
    const res = await fetch(`${API_URL}?action=upload_avatar`, {
      method: 'POST',
      headers: token ? { 'X-Auth-Token': token } : {},
      body: form,
    });
    const data = await res.json().catch(() => ({ error: 'Respuesta inválida del servidor' }));
    if (!res.ok || data.error) throw new Error(data.error || 'Error al subir la foto');
    this.persistSession(token, data.user);
    return data;
  },
};

function readFileAsDataURL(file) {
  return new Promise((resolve, reject) => {
    const reader = new FileReader();
    reader.onload = () => resolve(reader.result);
    reader.onerror = () => reject(new Error('No se pudo leer la imagen'));
    reader.readAsDataURL(file);
  });
}

async function compressImage(file, maxSize = 512) {
  if (!file || !file.type.startsWith('image/')) {
    throw new Error('Selecciona una imagen válida');
  }
  return new Promise((resolve, reject) => {
    const img = new Image();
    const url = URL.createObjectURL(file);
    img.onload = () => {
      try {
        let { width, height } = img;
        const scale = Math.min(1, maxSize / Math.max(width, height));
        width = Math.max(1, Math.round(width * scale));
        height = Math.max(1, Math.round(height * scale));
        const canvas = document.createElement('canvas');
        canvas.width = width;
        canvas.height = height;
        const ctx = canvas.getContext('2d');
        ctx.drawImage(img, 0, 0, width, height);
        canvas.toBlob(
          (blob) => {
            URL.revokeObjectURL(url);
            if (!blob) {
              reject(new Error('No se pudo procesar la imagen'));
              return;
            }
            resolve(new File([blob], 'avatar.jpg', { type: 'image/jpeg' }));
          },
          'image/jpeg',
          0.86
        );
      } catch (err) {
        URL.revokeObjectURL(url);
        reject(err);
      }
    };
    img.onerror = () => {
      URL.revokeObjectURL(url);
      reject(new Error('No se pudo cargar la imagen'));
    };
    img.src = url;
  });
}

function avatarUrl(user) {
  if (!user?.avatar) return null;
  if (user.avatar.startsWith('data:') || user.avatar.startsWith('http')) return user.avatar;
  return user.avatar;
}

const DataService = {
  routinesKey(userId) {
    return `rutinasimple_routines_${userId || 'guest'}`;
  },

  completionsKey(userId) {
    return `rutinasimple_completions_${userId || 'guest'}`;
  },

  async getAllRoutines(user) {
    if (USE_LOCAL_STORAGE) {
      const key = this.routinesKey(user?.id);
      const stored = localStorage.getItem(key);
      if (!stored) return [];
      return JSON.parse(stored);
    }
    return apiFetch('get_all');
  },
  
  async saveRoutine(routineData, user) {
    if (USE_LOCAL_STORAGE) {
      const key = this.routinesKey(user?.id);
      let routines = await this.getAllRoutines(user);
      let routineId = routineData.id;
      
      if (routineId) {
        const index = routines.findIndex(r => r.id === routineId);
        if (index > -1) {
          routineData.exercises = routineData.exercises.map((ex, i) => ({ ...ex, id: ex.id || Date.now() + i }));
          routines[index] = routineData;
        }
      } else {
        const newRoutine = {
          ...routineData,
          id: Date.now(),
          exercises: routineData.exercises.map((ex, i) => ({ ...ex, id: Date.now() + i }))
        };
        routines.push(newRoutine);
        routineId = newRoutine.id;
      }
      localStorage.setItem(key, JSON.stringify(routines));
      return { success: true, routine_id: routineId };
    }
    return apiFetch('save_routine', {
      method: 'POST',
      body: JSON.stringify(routineData),
    });
  },

  async deleteRoutine(id, user) {
    if (USE_LOCAL_STORAGE) {
      const key = this.routinesKey(user?.id);
      let routines = await this.getAllRoutines(user);
      routines = routines.filter(r => r.id !== id);
      localStorage.setItem(key, JSON.stringify(routines));
      return { success: true };
    }
    return apiFetch('delete_routine', {
      method: 'POST',
      body: JSON.stringify({ id }),
    });
  },

  _readLocalCompletions(user) {
    try {
      const raw = localStorage.getItem(this.completionsKey(user?.id));
      return raw ? JSON.parse(raw) : [];
    } catch {
      return [];
    }
  },

  _localIdsForDate(user, date) {
    return this._readLocalCompletions(user)
      .filter(c => c.completed_on === date)
      .map(c => Number(c.routine_id));
  },

  _saveLocalCompletion(user, routineId, date) {
    const key = this.completionsKey(user?.id);
    const all = this._readLocalCompletions(user);
    const id = Number(routineId);
    const exists = all.some(c => Number(c.routine_id) === id && c.completed_on === date);
    if (!exists) {
      all.push({ routine_id: id, completed_on: date });
      localStorage.setItem(key, JSON.stringify(all));
    }
  },

  async getCompletions(user, date = todayISO()) {
    const localIds = this._localIdsForDate(user, date);
    if (USE_LOCAL_STORAGE) {
      return { date, routine_ids: localIds };
    }
    try {
      const remote = await apiFetch(`get_completions&date=${encodeURIComponent(date)}`);
      const remoteIds = Array.isArray(remote?.routine_ids)
        ? remote.routine_ids.map(Number)
        : [];
      return { date, routine_ids: [...new Set([...remoteIds, ...localIds])] };
    } catch (err) {
      console.warn('Completions API:', err.message);
      return { date, routine_ids: localIds };
    }
  },

  async completeRoutine(routineId, user, date = todayISO()) {
    // Siempre guardamos en local para que el inicio se actualice aunque falle la API/tabla
    this._saveLocalCompletion(user, routineId, date);
    if (USE_LOCAL_STORAGE) {
      return { success: true, routine_id: Number(routineId), completed_on: date };
    }
    try {
      return await apiFetch('complete_routine', {
        method: 'POST',
        body: JSON.stringify({ routine_id: routineId, completed_on: date }),
      });
    } catch (err) {
      console.warn('complete_routine API:', err.message);
      return { success: true, routine_id: Number(routineId), completed_on: date };
    }
  },
};

// ==========================================
// COMPONENTES
// ==========================================

function AuthScreen({ onAuth }) {
  const [mode, setMode] = useState('login'); // login | register
  const [name, setName] = useState('');
  const [email, setEmail] = useState('');
  const [password, setPassword] = useState('');
  const [showPassword, setShowPassword] = useState(false);
  const [error, setError] = useState('');
  const [loading, setLoading] = useState(false);

  const switchMode = (next) => {
    setMode(next);
    setError('');
    setShowPassword(false);
  };

  const handleSubmit = async (e) => {
    e.preventDefault();
    setError('');
    setLoading(true);
    try {
      const data = mode === 'login'
        ? await AuthService.login({ email, password })
        : await AuthService.register({ name, email, password });
      onAuth(data.user);
    } catch (err) {
      setError(err.message || 'No se pudo continuar');
    } finally {
      setLoading(false);
    }
  };

  return (
    <div className="auth-screen screen-panel">
      <div className="auth-brand">
        <div className="auth-logo-wrap">
          <AppLogo size={64} />
        </div>
        <h1>Rutina<span>Simple</span></h1>
        <p>App gratis de rutinas de ejercicio. Fácil de usar, en casa o en el gym.</p>
      </div>

      <div className="auth-mode-toggle">
        <button type="button" className={mode === 'login' ? 'active' : ''} onClick={() => switchMode('login')}>
          Iniciar sesión
        </button>
        <button type="button" className={mode === 'register' ? 'active' : ''} onClick={() => switchMode('register')}>
          Crear cuenta
        </button>
      </div>

      <form className="auth-form" onSubmit={handleSubmit} key={mode}>
        {mode === 'register' && (
          <label className="auth-field">
            <span>Nombre</span>
            <input className="auth-input" placeholder="Tu nombre"
                   value={name} onChange={e => setName(e.target.value)} required
                   autoComplete="name" />
          </label>
        )}

        <label className="auth-field">
          <span>Email</span>
          <input className="auth-input" type="email" placeholder="tu@email.com"
                 value={email} onChange={e => setEmail(e.target.value)} required autoComplete="email" />
        </label>

        <label className="auth-field">
          <span>Contraseña</span>
          <div className="auth-password-wrap">
            <input className="auth-input" type={showPassword ? 'text' : 'password'}
                   placeholder={mode === 'register' ? 'Mínimo 6 caracteres' : 'Tu contraseña'}
                   value={password} onChange={e => setPassword(e.target.value)} required
                   minLength={6}
                   autoComplete={mode === 'login' ? 'current-password' : 'new-password'} />
            <button type="button" className="auth-eye" onClick={() => setShowPassword(v => !v)}
                    aria-label={showPassword ? 'Ocultar contraseña' : 'Mostrar contraseña'}>
              {showPassword ? 'Ocultar' : 'Mostrar'}
            </button>
          </div>
        </label>

        {error && (
          <div className="auth-error">
            {Icons.info}
            <span>{error}</span>
          </div>
        )}

        <button className="btn-primary auth-submit" type="submit" disabled={loading}>
          {loading ? 'Espera...' : (mode === 'login' ? 'Entrar' : 'Crear cuenta')}
        </button>

        <p className="auth-footnote">
          Tus rutinas quedan guardadas en tu cuenta.
        </p>
      </form>
    </div>
  );
}

function RestDayView({ currentDay, title, message, actionLabel, onAction, secondaryLabel, onSecondary }) {
  return (
    <div className="screen-panel today-screen is-centered">
      <div className="empty-state">
        <div className="emoji">
          <svg width="46" height="46" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round">
            <path d="M12 3a6 6 0 0 0 9 9 9 9 0 1 1-9-9Z"/>
          </svg>
        </div>
        <h2>{title || `Hoy es ${DAYS_OF_WEEK[currentDay]}`}</h2>
        <p className="empty-state-msg">{message}</p>
        {onAction && actionLabel && (
          <button className="btn-primary btn-cta empty-state-cta" onClick={onAction}>
            {Icons.plus} {actionLabel}
          </button>
        )}
        {onSecondary && secondaryLabel && (
          <button className="btn-secondary btn-cta empty-state-cta-secondary" onClick={onSecondary}>
            {Icons.play} {secondaryLabel}
          </button>
        )}
      </div>
    </div>
  );
}

function TodayRoutine({ routines, completedIds, onStart, onGoToRoutines }) {
  const currentDay = new Date().getDay();
  const todaysRoutines = routines.filter(r => r.day_of_week === currentDay);
  const doneSet = new Set((completedIds || []).map(Number));
  const pendingToday = todaysRoutines.filter(r => !doneSet.has(Number(r.id)));
  const allTodayDone = todaysRoutines.length > 0 && pendingToday.length === 0;

  const [selectedRoutine, setSelectedRoutine] = useState(null);

  useEffect(() => {
    if (pendingToday.length === 0) {
      setSelectedRoutine(null);
      return;
    }
    setSelectedRoutine(prev => {
      if (prev && pendingToday.some(r => r.id === prev.id)) {
        return pendingToday.find(r => r.id === prev.id) || pendingToday[0];
      }
      return pendingToday[0];
    });
  }, [routines, currentDay, completedIds]);

  // Sin rutinas en la cuenta, o sin rutina para hoy → descanso
  if (routines.length === 0) {
    return (
      <RestDayView
        currentDay={currentDay}
        message="Hoy es buen día para descansar. Cuando quieras, crea una rutina y empieza a entrenar."
        actionLabel="Crear rutina"
        onAction={onGoToRoutines}
      />
    );
  }

  if (todaysRoutines.length === 0) {
    return (
      <RestDayView
        currentDay={currentDay}
        message="Hoy no toca entrenar. Descansa, recupera y vuelve con energía."
        actionLabel="Ir a Rutinas"
        onAction={onGoToRoutines}
      />
    );
  }

  // Todas las de hoy completadas → mensaje de descanso (sin panel de completada)
  if (allTodayDone) {
    const lastDone = todaysRoutines[0];
    return (
      <RestDayView
        currentDay={currentDay}
        title="Rutina de hoy completada"
        message="¡Bien hecho! Ve y descansa, te lo mereces. Mañana será otro día."
        secondaryLabel="Entrenar de nuevo"
        onSecondary={() => onStart(lastDone)}
      />
    );
  }

  if (!selectedRoutine) return null;

  const exerciseCount = selectedRoutine.exercises?.length || 0;
  const totalSets = (selectedRoutine.exercises || []).reduce((sum, ex) => sum + (Number(ex.sets) || 0), 0);
  const otherPending = pendingToday.filter(r => r.id !== selectedRoutine.id);
  const doneToday = todaysRoutines.filter(r => doneSet.has(Number(r.id))).length;
  const dayProgress = Math.round((doneToday / todaysRoutines.length) * 100);
  const setsFill = Math.min(100, Math.round((totalSets / Math.max(totalSets, 12)) * 100));
  const exerciseFill = Math.min(100, Math.round((exerciseCount / Math.max(exerciseCount, 6)) * 100));

  return (
    <div className="screen-panel today-screen">
      <div className="header header-titles">
        <div className="today-greeting">
          <p>Listo para moverte</p>
          <h1>Hoy · {DAYS_OF_WEEK[currentDay]}</h1>
        </div>
      </div>

      <div className="hero-card" onClick={() => onStart(selectedRoutine)} role="button" tabIndex={0}
           onKeyDown={e => { if (e.key === 'Enter' || e.key === ' ') onStart(selectedRoutine); }}>
        <div className="hero-card-body">
          <span className="hero-card-label">Tu entrenamiento</span>
          <h3>{selectedRoutine.name}</h3>
          <p>{exerciseCount} ejercicios · {totalSets} series</p>
          <span className="hero-cta">{Icons.play} Empezar ahora</span>
        </div>
        <div className="play-icon">{Icons.play}</div>
      </div>

      <section className="neu-today-card" aria-label="Resumen de hoy">
        <h3 className="neu-today-card-title">Resumen</h3>

        <div className="neu-metric-row">
          <div className="neu-metric-icon lime" aria-hidden="true">{Icons.dumbbell}</div>
          <div className="neu-metric-body">
            <div className="neu-metric-head">
              <span>Ejercicios</span>
              <strong>{exerciseCount}</strong>
            </div>
            <div className="neu-progress" role="progressbar" aria-valuenow={exerciseCount} aria-valuemin={0} aria-valuemax={Math.max(exerciseCount, 6)}>
              <div className="neu-progress-fill" style={{ width: `${exerciseFill}%` }} />
            </div>
          </div>
        </div>

        <div className="neu-metric-row">
          <div className="neu-metric-icon energy" aria-hidden="true">{Icons.repeat}</div>
          <div className="neu-metric-body">
            <div className="neu-metric-head">
              <span>Series</span>
              <strong>{totalSets}</strong>
            </div>
            <div className="neu-progress" role="progressbar" aria-valuenow={totalSets} aria-valuemin={0} aria-valuemax={Math.max(totalSets, 12)}>
              <div className="neu-progress-fill is-energy" style={{ width: `${setsFill}%` }} />
            </div>
          </div>
        </div>

        <div className="neu-metric-row">
          <div className="neu-metric-icon mint" aria-hidden="true">{Icons.check}</div>
          <div className="neu-metric-body">
            <div className="neu-metric-head">
              <span>Progreso del día</span>
              <strong>{doneToday}/{todaysRoutines.length}</strong>
            </div>
            <div className="neu-progress" role="progressbar" aria-valuenow={dayProgress} aria-valuemin={0} aria-valuemax={100}>
              <div className="neu-progress-fill is-mint" style={{ width: `${dayProgress}%` }} />
            </div>
          </div>
        </div>
      </section>

      {otherPending.length > 0 && (
        <>
          <p className="today-section-label">Otras de hoy</p>
          <div className="workout-list">
            {otherPending.map(r => (
              <button
                key={r.id}
                type="button"
                className={`alt-routine ${selectedRoutine.id === r.id ? 'active-alt' : ''}`}
                onClick={() => setSelectedRoutine(r)}
              >
                <div className="routine-thumb">{Icons.routines}</div>
                <div>
                  <strong>{r.name}</strong>
                  <span>{r.exercises?.length || 0} ejercicios · tocar para elegir</span>
                </div>
              </button>
            ))}
          </div>
        </>
      )}
    </div>
  );
}

function ActiveTracker({ routine, onFinish, onBack }) {
  const [currentExIndex, setCurrentExIndex] = useState(0);
  const [currentRep, setCurrentRep] = useState(0);
  const [currentSet, setCurrentSet] = useState(1);
  const [isResting, setIsResting] = useState(false);
  const [isRunning, setIsRunning] = useState(false);
  const [timeLeft, setTimeLeft] = useState(0);
  const [showHelp, setShowHelp] = useState(false);
  const finishingRef = useRef(false);

  const exercise = routine.exercises[currentExIndex];

  const finishSet = () => {
    if (finishingRef.current) return;
    finishingRef.current = true;
    setCurrentRep(0);
    setIsRunning(false);
    if (currentSet >= exercise.sets) {
      if (currentExIndex + 1 >= routine.exercises.length) {
        SoundFx.workoutDone();
        onFinish();
      } else {
        setIsResting(true);
        setCurrentSet(1);
        setCurrentExIndex(prev => prev + 1);
      }
    } else {
      setIsResting(true);
      setCurrentSet(prev => prev + 1);
    }
    // Permite el siguiente ciclo (descanso → serie) sin bloquear
    queueMicrotask(() => { finishingRef.current = false; });
  };

  // Cuenta regresiva para ejercicios por tiempo (se pausa al abrir la ayuda)
  useEffect(() => {
    if (!isRunning || showHelp) return;
    if (timeLeft <= 0) {
      setIsRunning(false);
      SoundFx.setDone();
      finishSet();
      return;
    }
    SoundFx.tick(timeLeft <= 3);
    const timer = setInterval(() => setTimeLeft(prev => prev - 1), 1000);
    return () => clearInterval(timer);
  }, [isRunning, timeLeft, showHelp]);

  // Mantener audio desbloqueado mientras se entrena (toques en pantalla)
  useEffect(() => {
    const keepAlive = () => SoundFx.unlock();
    window.addEventListener('touchstart', keepAlive, { passive: true });
    window.addEventListener('mousedown', keepAlive);
    return () => {
      window.removeEventListener('touchstart', keepAlive);
      window.removeEventListener('mousedown', keepAlive);
    };
  }, []);

  if (!exercise) return null;

  const handleTap = () => {
    SoundFx.unlock();
    if (exercise.is_time) {
      if (isRunning) {
        setIsRunning(false);
        SoundFx.setDone();
        finishSet();
      } else {
        setTimeLeft(Number(exercise.reps) || 0);
        setIsRunning(true);
        SoundFx.start();
      }
      return;
    }
    if (currentRep + 1 >= exercise.reps) {
      SoundFx.setDone();
      finishSet();
    } else {
      SoundFx.rep();
      setCurrentRep(prev => prev + 1);
    }
  };

  if (isResting) {
    return <RestTimer seconds={exercise.rest_secs} onSkip={() => setIsResting(false)} />;
  }

  const R = 140;
  const CIRC = 2 * Math.PI * R;
  const totalTime = Number(exercise.reps) || 0;
  const repProgress = exercise.is_time
    ? (isRunning && totalTime > 0 ? (totalTime - timeLeft) / totalTime : 0)
    : currentRep / Math.max(exercise.reps, 1);

  return (
    <div className="active-tracker">
      <header className="tracker-head">
        <p className="tracker-kicker">Ejercicio {currentExIndex + 1} de {routine.exercises.length}</p>
        <h2 className="tracker-title">{exercise.name}</h2>
        <p className="tracker-set">Serie {currentSet} de {exercise.sets}</p>
      </header>

      <main className="tracker-stage">
        <button type="button" className="help-btn" onClick={() => setShowHelp(true)}>
          {Icons.help} Cómo se hace
        </button>
        <div className="giant-wrap">
          <svg className="giant-ring" viewBox="0 0 300 300" aria-hidden="true">
            <circle className="track" cx="150" cy="150" r={R} strokeWidth="14" />
            <circle className="progress" cx="150" cy="150" r={R} strokeWidth="14"
              strokeDasharray={CIRC}
              strokeDashoffset={CIRC * (1 - repProgress)} />
          </svg>
          <button type="button" className="giant-btn" onClick={handleTap}>
            <div className="rep-count">
              {exercise.is_time ? (isRunning ? timeLeft : "GO") : currentRep}
            </div>
            <div className="rep-label">
              {exercise.is_time ? (isRunning ? "Toca para terminar" : "Iniciar") : `de ${exercise.reps}`}
            </div>
          </button>
        </div>
      </main>

      <footer className="tracker-footer">
        <div className="sets-container" aria-hidden={exercise.sets <= 1}>
          {Array.from({ length: exercise.sets }).map((_, i) => (
            <div key={i} className={`set-dot ${i < currentSet - 1 ? 'active' : ''}`}></div>
          ))}
        </div>
        <button type="button" className="btn-secondary" onClick={onBack}>Cerrar Entrenamiento</button>
      </footer>

      {showHelp && (
        <div className="help-overlay" onClick={() => setShowHelp(false)}>
          <div className="help-sheet" onClick={e => e.stopPropagation()}>
            <p className="help-sheet-label">Ayuda</p>
            <h3>{exercise.name}</h3>
            <p>{exercise.description || 'No hay descripción para este ejercicio. Puedes añadirla al editar la rutina.'}</p>
            <button type="button" className="btn-primary" onClick={() => setShowHelp(false)}>Volver al ejercicio</button>
          </div>
        </div>
      )}
    </div>
  );
}

function RestTimer({ seconds, onSkip }) {
  const [timeLeft, setTimeLeft] = useState(seconds);
  const skippedRef = useRef(false);
  const onSkipRef = useRef(onSkip);
  onSkipRef.current = onSkip;

  const endRest = (playCue) => {
    if (skippedRef.current) return;
    skippedRef.current = true;
    if (playCue) SoundFx.restDone();
    onSkipRef.current();
  };

  useEffect(() => {
    if (timeLeft <= 0) {
      endRest(true);
      return;
    }
    SoundFx.tick(timeLeft <= 3);
    const timer = setInterval(() => setTimeLeft(prev => prev - 1), 1000);
    return () => clearInterval(timer);
  }, [timeLeft]);

  useEffect(() => {
    const keepAlive = () => SoundFx.unlock();
    window.addEventListener('touchstart', keepAlive, { passive: true });
    return () => window.removeEventListener('touchstart', keepAlive);
  }, []);

  const formatTime = (secs) => {
    const m = Math.floor(secs / 60);
    const s = secs % 60;
    return `${m}:${s.toString().padStart(2, '0')}`;
  };

  const R = 130;
  const CIRC = 2 * Math.PI * R;
  const progress = seconds > 0 ? timeLeft / seconds : 0;

  return (
    <div className="active-tracker">
      <header className="tracker-head">
        <p className="tracker-kicker">Recupera el aliento</p>
        <h2 className="tracker-title">Descanso</h2>
      </header>

      <main className="tracker-stage tracker-stage-rest">
        <div className="timer-wrap">
          <svg className="timer-ring" viewBox="0 0 280 280" aria-hidden="true">
            <circle className="track" cx="140" cy="140" r={R} strokeWidth="12" />
            <circle className="progress" cx="140" cy="140" r={R} strokeWidth="12"
              strokeDasharray={CIRC}
              strokeDashoffset={CIRC * (1 - progress)} />
          </svg>
          <div className="timer-core">
            <div className="time-left">{formatTime(timeLeft)}</div>
          </div>
        </div>
      </main>

      <footer className="tracker-footer">
        <button type="button" className="btn-primary" onClick={() => endRest(false)}>
          Continuar Ya
        </button>
      </footer>
    </div>
  );
}

function RoutinesScreen({ routines, onDataChange, user, onEditingChange }) {
  const [isEditing, setIsEditing] = useState(false);
  const [currentRoutine, setCurrentRoutine] = useState({ name: '', day_of_week: 1, exercises: [] });
  // null | 'menu' | 'body' | 'list'
  const [pickerStep, setPickerStep] = useState(null);
  const [selectedBodyPart, setSelectedBodyPart] = useState(null);

  const setEditing = (next) => {
    setIsEditing(next);
    if (onEditingChange) onEditingChange(next);
  };

  const handleDelete = async (e, id) => {
    e.stopPropagation();
    if (confirm("¿Estás seguro de eliminar esta rutina?")) {
      await DataService.deleteRoutine(id, user);
      onDataChange();
    }
  };

  const startEdit = (routine) => {
    setCurrentRoutine(JSON.parse(JSON.stringify(routine)));
    setPickerStep(null);
    setEditing(true);
  };

  const startCreate = () => {
    setCurrentRoutine({ name: '', day_of_week: 1, exercises: [] });
    setPickerStep(null);
    setEditing(true);
  };

  const addExercise = (exercise) => {
    setCurrentRoutine(prev => ({
      ...prev,
      exercises: [...prev.exercises, exercise]
    }));
    setPickerStep(null);
    setSelectedBodyPart(null);
  };

  const handleAddCustom = () => addExercise(emptyExercise());

  const handlePickFromCatalog = (item) => addExercise(exerciseFromCatalog(item));

  const handleRemoveExercise = (index) => {
    const copy = [...currentRoutine.exercises];
    copy.splice(index, 1);
    setCurrentRoutine({ ...currentRoutine, exercises: copy });
  };

  const updateExercise = (index, field, value) => {
    const copy = currentRoutine.exercises.map((ex, i) => i === index ? { ...ex, [field]: value } : ex);
    setCurrentRoutine({ ...currentRoutine, exercises: copy });
  };

  const handleSave = async () => {
    if (!currentRoutine.name || currentRoutine.exercises.length === 0) {
      return alert("Falta el nombre de la rutina o al menos un ejercicio.");
    }
    await DataService.saveRoutine(currentRoutine, user);
    setEditing(false);
    setPickerStep(null);
    onDataChange();
  };

  const catalogForPart = selectedBodyPart
    ? EXERCISE_CATALOG.filter(ex => ex.bodyPart === selectedBodyPart)
    : [];

  const bodyPartName = BODY_PARTS.find(b => b.id === selectedBodyPart)?.name || '';

  if (isEditing) {
    return (
      <div className="screen-panel">
        <div className="header header-titles">
          <h2>{currentRoutine.id ? 'Editar' : 'Nueva'} Rutina</h2>
        </div>
        
        <input className="neu-input" placeholder="Nombre (Ej: Pierna Intensa)" 
               value={currentRoutine.name} onChange={e => setCurrentRoutine({...currentRoutine, name: e.target.value})} />
        
        <select className="neu-input" value={currentRoutine.day_of_week} 
                onChange={e => setCurrentRoutine({...currentRoutine, day_of_week: parseInt(e.target.value)})}>
          {DAYS_OF_WEEK.map((d, i) => <option key={i} value={i}>{d}</option>)}
        </select>

        <div style={{display:'flex', justifyContent:'space-between', alignItems:'center', marginTop: 20, marginBottom: 15}}>
          <h3 style={{fontSize:'1.1rem', fontWeight:700}}>Ejercicios ({currentRoutine.exercises.length})</h3>
        </div>

        {currentRoutine.exercises.map((ex, i) => (
          <div key={i} className="config-exercise-card neu-out">
            <div className="ex-card-head">
              <input className="neu-input ex-name-input" placeholder="Nombre del ejercicio"
                     value={ex.name} onChange={e => updateExercise(i, 'name', e.target.value)}/>
              <button type="button" className="ex-remove-btn" aria-label="Quitar ejercicio"
                      onClick={() => handleRemoveExercise(i)}>{Icons.close}</button>
            </div>

            <textarea className="neu-input ex-desc-input"
                      placeholder="Descripción (opcional): técnica, notas, músculos trabajados..."
                      value={ex.description || ''} onChange={e => updateExercise(i, 'description', e.target.value)}/>

            <p className="field-label">Tipo de ejercicio</p>
            <div className="type-toggle">
              <button type="button" className={!ex.is_time ? 'active' : ''} onClick={() => updateExercise(i, 'is_time', false)}>
                {Icons.repeat} Repeticiones
              </button>
              <button type="button" className={ex.is_time ? 'active is-time-mode' : ''} onClick={() => updateExercise(i, 'is_time', true)}>
                {Icons.clock} Por tiempo
              </button>
            </div>
            <p className="field-hint">
              {Icons.info}
              <span>{ex.is_time
                ? 'Mantienes el ejercicio durante los segundos indicados (ej. plancha).'
                : 'Cuentas un número fijo de repeticiones por serie (ej. flexiones).'}</span>
            </p>

            <div className="ex-metrics">
              <label className="ex-metric">
                <span className="ex-metric-label">Series</span>
                <input type="number" min="1" inputMode="numeric" className="neu-input small" value={ex.sets}
                       onChange={e => updateExercise(i, 'sets', e.target.value)}/>
              </label>
              <label className="ex-metric">
                <span className="ex-metric-label">{ex.is_time ? 'Segundos' : 'Reps'}</span>
                <input type="number" min="1" inputMode="numeric" className="neu-input small" value={ex.reps}
                       onChange={e => updateExercise(i, 'reps', e.target.value)}/>
              </label>
              <label className="ex-metric">
                <span className="ex-metric-label">Pausa</span>
                <input type="number" min="0" inputMode="numeric" className="neu-input small" value={ex.rest_secs}
                       onChange={e => updateExercise(i, 'rest_secs', e.target.value)}/>
                <span className="ex-metric-unit">seg</span>
              </label>
            </div>
          </div>
        ))}

        {/* Selector: catálogo o personalizado */}
        {pickerStep === null && (
          <button className="btn-secondary" onClick={() => setPickerStep('menu')} style={{marginBottom:'28px'}}>
            {Icons.plus} Añadir Ejercicio
          </button>
        )}

        {pickerStep === 'menu' && (
          <div className="picker-sheet" style={{marginBottom:28}}>
            <h3>Añadir ejercicio</h3>
            <p className="picker-sub">Elige del catálogo o crea uno tuyo.</p>
            <div className="picker-actions">
              <button className="btn-primary" onClick={() => setPickerStep('body')}>
                {Icons.catalog} Desde el catálogo
              </button>
              <button className="btn-secondary" onClick={handleAddCustom}>
                {Icons.plus} Crear personalizado
              </button>
              <button className="btn-secondary" onClick={() => setPickerStep(null)}>Cancelar</button>
            </div>
          </div>
        )}

        {pickerStep === 'body' && (
          <div className="picker-sheet" style={{marginBottom:28}}>
            <button className="picker-back" onClick={() => setPickerStep('menu')}>{Icons.back} Volver</button>
            <h3>Parte del cuerpo</h3>
            <p className="picker-sub">Selecciona el grupo muscular.</p>
            <div className="picker-grid">
              {BODY_PARTS.map(part => (
                <button key={part.id} className="picker-chip" onClick={() => {
                  setSelectedBodyPart(part.id);
                  setPickerStep('list');
                }}>
                  {part.name}
                </button>
              ))}
            </div>
          </div>
        )}

        {pickerStep === 'list' && (
          <div className="picker-sheet" style={{marginBottom:28}}>
            <button className="picker-back" onClick={() => setPickerStep('body')}>{Icons.back} {bodyPartName}</button>
            <h3>Ejercicios de {bodyPartName}</h3>
            <p className="picker-sub">Toca uno para añadirlo a la rutina.</p>
            <div className="picker-list">
              {catalogForPart.map(item => (
                <button key={item.id} className="picker-item" onClick={() => handlePickFromCatalog(item)}>
                  <div className="picker-item-text">
                    <strong>{item.name}</strong>
                    <span>{item.description}</span>
                  </div>
                  <span className={`picker-item-meta ${item.is_time ? 'is-time' : 'is-reps'}`}>
                    {item.is_time ? <>{Icons.clock} Tiempo</> : <>{Icons.repeat} Reps</>}
                  </span>
                </button>
              ))}
            </div>
          </div>
        )}
        
        <div className="flex-row form-actions" style={{gap: '14px', marginBottom: 0}}>
          <button className="btn-secondary" onClick={() => { setEditing(false); setPickerStep(null); }}>Cancelar</button>
          <button className="btn-primary" onClick={handleSave}>Guardar</button>
        </div>
      </div>
    );
  }

  return (
    <div className={`screen-panel routines-screen ${routines.length === 0 ? 'is-empty' : ''}`}>
      <div className="header header-titles">
        <div>
          <p>Tu plan</p>
          <h1>Mis Rutinas</h1>
        </div>
      </div>

      <div className="config-main">
        <button className="btn-primary" onClick={startCreate}>
          {Icons.plus} Nueva Rutina
        </button>

        <div className="workout-list">
          {routines.length === 0 && (
            <p className="empty-routines-hint">Aún no tienes rutinas. Crea la primera para empezar.</p>
          )}
          {routines.map(r => (
            <div key={r.id} className="workout-card neu-out" style={{flexDirection: 'column', alignItems: 'flex-start', cursor:'default'}}>
              <div className="routine-card-top">
                <div style={{display:'flex', alignItems:'center', gap: 12, minWidth: 0}}>
                  <div className="routine-thumb">{Icons.dumbbell}</div>
                  <h3 style={{marginBottom: 0}}>{r.name}</h3>
                </div>
                <span className="day-badge">{DAYS_OF_WEEK[r.day_of_week]}</span>
              </div>

              <div className="routine-card-bottom">
                <p>{r.exercises?.length || 0} ejercicios</p>
                <div className="routine-card-actions">
                  <button className="btn-secondary btn-compact" onClick={() => startEdit(r)}>Editar</button>
                  <button className="btn-danger btn-compact" onClick={(e) => handleDelete(e, r.id)}>Borrar</button>
                </div>
              </div>
            </div>
          ))}
        </div>
      </div>
    </div>
  );
}

function usePwaInstall() {
  const [deferred, setDeferred] = useState(null);
  const [installed, setInstalled] = useState(() => {
    try {
      return window.matchMedia('(display-mode: standalone)').matches
        || window.navigator.standalone === true;
    } catch {
      return false;
    }
  });
  const [showIosHelp, setShowIosHelp] = useState(false);

  const isIos = typeof navigator !== 'undefined'
    && /iphone|ipad|ipod/i.test(navigator.userAgent)
    && !window.MSStream;
  const isAndroid = typeof navigator !== 'undefined'
    && /android/i.test(navigator.userAgent);

  useEffect(() => {
    const onPrompt = (e) => {
      e.preventDefault();
      setDeferred(e);
    };
    const onInstalled = () => {
      setInstalled(true);
      setDeferred(null);
    };
    window.addEventListener('beforeinstallprompt', onPrompt);
    window.addEventListener('appinstalled', onInstalled);
    return () => {
      window.removeEventListener('beforeinstallprompt', onPrompt);
      window.removeEventListener('appinstalled', onInstalled);
    };
  }, []);

  const install = async () => {
    if (deferred) {
      deferred.prompt();
      const choice = await deferred.userChoice;
      if (choice?.outcome === 'accepted') setInstalled(true);
      setDeferred(null);
      return;
    }
    if (isIos) setShowIosHelp(true);
  };

  return {
    installed,
    canInstall: !!deferred || (isIos && !installed),
    isIos,
    isAndroid,
    showIosHelp,
    setShowIosHelp,
    install,
  };
}

function SettingsScreen({ theme, toggleTheme, user, onLogout, onUserUpdate }) {
  const pwa = usePwaInstall();
  const fileRef = useRef(null);
  const nameInputRef = useRef(null);
  const [name, setName] = useState(user?.name || '');
  const [editingName, setEditingName] = useState(false);
  const [saving, setSaving] = useState(false);
  const [uploading, setUploading] = useState(false);
  const [profileMsg, setProfileMsg] = useState('');
  const [profileErr, setProfileErr] = useState('');
  const [soundOn, setSoundOn] = useState(() => normalizeSoundPrefs(user).sound_enabled);
  const [soundSaving, setSoundSaving] = useState(false);
  const [soundErr, setSoundErr] = useState('');
  const skipNameBlurSave = useRef(false);
  const [feedbackOpen, setFeedbackOpen] = useState(false);
  const [feedbackCategory, setFeedbackCategory] = useState('sugerencia');
  const [feedbackMessage, setFeedbackMessage] = useState('');
  const [feedbackContact, setFeedbackContact] = useState(user?.email || '');
  const [feedbackSending, setFeedbackSending] = useState(false);
  const [feedbackMsg, setFeedbackMsg] = useState('');
  const [feedbackErr, setFeedbackErr] = useState('');

  useEffect(() => {
    if (!editingName) setName(user?.name || '');
    const prefs = normalizeSoundPrefs(user);
    setSoundOn(prefs.sound_enabled);
    SoundFx.applyFromUser(prefs);
    if (!feedbackOpen) setFeedbackContact(user?.email || '');
  }, [user?.name, user?.email, user?.sound_enabled, editingName, feedbackOpen]);

  const handleSendFeedback = async (e) => {
    e.preventDefault();
    setFeedbackMsg('');
    setFeedbackErr('');
    const trimmed = feedbackMessage.trim();
    if (trimmed.length < 10) {
      setFeedbackErr('Escribe al menos 10 caracteres');
      return;
    }
    setFeedbackSending(true);
    try {
      const data = await AuthService.sendFeedback({
        category: feedbackCategory,
        message: trimmed,
        contact_email: feedbackContact.trim(),
      });
      setFeedbackMsg(data.message || 'Gracias, recibimos tu mensaje');
      setFeedbackMessage('');
      setFeedbackCategory('sugerencia');
    } catch (err) {
      setFeedbackErr(err.message || 'No se pudo enviar');
    } finally {
      setFeedbackSending(false);
    }
  };

  useEffect(() => {
    if (editingName) {
      nameInputRef.current?.focus();
      nameInputRef.current?.select();
    }
  }, [editingName]);

  const toggleSound = async () => {
    const next = !soundOn;
    setSoundOn(next);
    SoundFx.setEnabled(next);
    if (next) SoundFx.preview();
    setSoundSaving(true);
    setSoundErr('');
    try {
      const data = await AuthService.updateSoundPrefs({ sound_enabled: next });
      onUserUpdate?.(data.user);
    } catch (e) {
      setSoundErr(e.message || 'No se pudieron guardar los sonidos');
      const prefs = normalizeSoundPrefs(user);
      setSoundOn(prefs.sound_enabled);
      SoundFx.applyFromUser(prefs);
    } finally {
      setSoundSaving(false);
    }
  };

  const photo = avatarUrl(user);

  const startEditName = () => {
    if (saving) return;
    setProfileMsg('');
    setProfileErr('');
    setName(user?.name || '');
    setEditingName(true);
  };

  const cancelEditName = () => {
    skipNameBlurSave.current = true;
    setName(user?.name || '');
    setEditingName(false);
    setProfileErr('');
  };

  const handleSaveName = async () => {
    if (skipNameBlurSave.current) {
      skipNameBlurSave.current = false;
      return;
    }
    const trimmed = name.trim();
    if (!trimmed) {
      setProfileErr('El nombre no puede estar vacío');
      nameInputRef.current?.focus();
      return;
    }
    if (trimmed === (user?.name || '').trim()) {
      setEditingName(false);
      setProfileErr('');
      return;
    }
    setProfileErr('');
    setProfileMsg('');
    setSaving(true);
    try {
      const data = await AuthService.updateProfile({ name: trimmed });
      onUserUpdate?.(data.user);
      setName(data.user?.name || trimmed);
      setEditingName(false);
      setProfileMsg('Nombre actualizado');
    } catch (e) {
      setProfileErr(e.message || 'No se pudo guardar');
      nameInputRef.current?.focus();
    } finally {
      setSaving(false);
    }
  };

  const handleNameKeyDown = (e) => {
    if (e.key === 'Enter') {
      e.preventDefault();
      e.currentTarget.blur();
    } else if (e.key === 'Escape') {
      e.preventDefault();
      cancelEditName();
    }
  };

  const handlePickPhoto = async (e) => {
    const file = e.target.files?.[0];
    e.target.value = '';
    if (!file) return;
    setProfileErr('');
    setProfileMsg('');
    setUploading(true);
    try {
      const compressed = await compressImage(file);
      const data = await AuthService.uploadAvatar(compressed);
      onUserUpdate?.(data.user);
      setProfileMsg('Foto actualizada');
    } catch (e2) {
      setProfileErr(e2.message || 'No se pudo subir la foto');
    } finally {
      setUploading(false);
    }
  };

  return (
    <div className="screen-panel settings-screen">
      <div className="header header-titles">
        <div>
          <p>Cuenta</p>
          <h1>Ajustes</h1>
        </div>
      </div>

      <div className="settings-list">
        <div className="neu-out profile-card">
          <div className="profile-top">
            <button
              type="button"
              className="avatar-btn"
              onClick={() => fileRef.current?.click()}
              disabled={uploading}
              aria-label="Cambiar foto de perfil"
            >
              {photo ? (
                <img src={photo} alt="" className="avatar-img" />
              ) : (
                <span className="avatar-fallback">{Icons.user}</span>
              )}
              <span className="avatar-edit">{uploading ? '…' : Icons.plus}</span>
            </button>
            <div className="profile-meta">
              {editingName ? (
                <input
                  ref={nameInputRef}
                  className="neu-input profile-name-input"
                  value={name}
                  onChange={e => setName(e.target.value)}
                  onBlur={handleSaveName}
                  onKeyDown={handleNameKeyDown}
                  maxLength={120}
                  placeholder="Tu nombre"
                  autoComplete="name"
                  disabled={saving}
                  aria-label="Editar nombre"
                />
              ) : (
                <button
                  type="button"
                  className="profile-name-btn"
                  onClick={startEditName}
                  aria-label="Editar nombre"
                >
                  <span className="profile-name-row">
                    <strong>{user?.name || 'Usuario'}</strong>
                    <span className="profile-edit-icon" aria-hidden="true">{Icons.edit}</span>
                  </span>
                </button>
              )}
              <span className="profile-email">{user?.email || ''}</span>
            </div>
          </div>

          <input
            ref={fileRef}
            type="file"
            accept="image/jpeg,image/png,image/webp"
            hidden
            onChange={handlePickPhoto}
          />

          {(profileMsg || profileErr) && (
            <p className={`profile-feedback ${profileErr ? 'is-error' : ''}`}>
              {profileErr || profileMsg}
            </p>
          )}
        </div>

        <div className="settings-row neu-out">
          <div className="settings-row-text">
            <strong>Apariencia</strong>
            <span>{theme === 'dark' ? 'Tema oscuro' : 'Tema claro'}</span>
          </div>
          <button className="theme-toggle" onClick={toggleTheme} aria-label="Cambiar tema">
            {theme === 'light' ? Icons.moon : Icons.sun}
          </button>
        </div>

        <div className="settings-row neu-out">
          <div className="settings-row-text">
            <strong>Sonidos</strong>
            <span>
              {soundOn ? 'Activos en el entrenamiento' : 'Silenciados'}
              {soundSaving ? ' · guardando…' : ''}
            </span>
            {soundErr && (
              <span className="sound-inline-error">{soundErr}</span>
            )}
          </div>
          <button
            className="theme-toggle"
            onClick={toggleSound}
            aria-label={soundOn ? 'Silenciar sonidos' : 'Activar sonidos'}
            aria-pressed={soundOn}
          >
            {soundOn ? Icons.volume : Icons.volumeOff}
          </button>
        </div>

        <div className="neu-out install-block">
          <div className="install-row">
            <div className="settings-row-text">
              <strong>Instalar app</strong>
              <span>
                {pwa.installed
                  ? 'Ya la tienes en tu pantalla de inicio'
                  : 'Acceso directo sin pasar por la tienda'}
              </span>
            </div>
            {pwa.installed ? (
              <span className="install-ok" aria-label="Instalada">{Icons.check}</span>
            ) : (
              <button type="button" className="btn-secondary btn-compact" onClick={pwa.install}>
                {Icons.download} {pwa.isIos ? 'Cómo' : 'Instalar'}
              </button>
            )}
          </div>

          {pwa.showIosHelp && !pwa.installed && (
            <div className="install-guide">
              <p className="install-guide-title">En Safari:</p>
              <ol className="install-guide-list">
                <li>Toca <strong>Compartir</strong> {Icons.share} abajo.</li>
                <li>Elige <strong>Añadir a pantalla de inicio</strong>.</li>
                <li>Pulsa <strong>Añadir</strong>.</li>
              </ol>
              <button type="button" className="install-guide-close" onClick={() => pwa.setShowIosHelp(false)}>
                Cerrar
              </button>
            </div>
          )}

          {!pwa.installed && !pwa.isIos && !pwa.canInstall && (
            <p className="install-hint">
              Menú ⋮ del navegador → “Instalar app” o “Añadir a la pantalla de inicio”.
            </p>
          )}
        </div>

        <div className="neu-out feedback-block">
          <button
            type="button"
            className="feedback-toggle"
            onClick={() => {
              setFeedbackOpen(v => !v);
              setFeedbackMsg('');
              setFeedbackErr('');
            }}
            aria-expanded={feedbackOpen}
          >
            <div className="settings-row-text">
              <strong>Soporte y feedback</strong>
              <span>Cuéntanos ideas, problemas o mejoras</span>
            </div>
            <span className={`feedback-chevron ${feedbackOpen ? 'is-open' : ''}`} aria-hidden="true">
              {Icons.message}
            </span>
          </button>

          {feedbackOpen && (
            <form className="feedback-form" onSubmit={handleSendFeedback}>
              <label className="feedback-field">
                <span>Tipo</span>
                <select
                  className="neu-input"
                  value={feedbackCategory}
                  onChange={e => setFeedbackCategory(e.target.value)}
                  disabled={feedbackSending}
                >
                  <option value="sugerencia">Sugerencia</option>
                  <option value="problema">Problema / error</option>
                  <option value="elogio">Elogio</option>
                  <option value="otro">Otro</option>
                </select>
              </label>

              <label className="feedback-field">
                <span>Tu mensaje</span>
                <textarea
                  className="neu-input feedback-textarea"
                  placeholder="¿Qué te gustaría mejorar o reportar?"
                  value={feedbackMessage}
                  onChange={e => setFeedbackMessage(e.target.value)}
                  maxLength={4000}
                  rows={4}
                  disabled={feedbackSending}
                  required
                />
              </label>

              <label className="feedback-field">
                <span>Email de respuesta</span>
                <input
                  className="neu-input"
                  type="email"
                  value={feedbackContact}
                  onChange={e => setFeedbackContact(e.target.value)}
                  placeholder="tu@email.com"
                  disabled={feedbackSending}
                  autoComplete="email"
                />
              </label>

              {(feedbackMsg || feedbackErr) && (
                <p className={`profile-feedback ${feedbackErr ? 'is-error' : ''}`}>
                  {feedbackErr || feedbackMsg}
                </p>
              )}

              <button className="btn-primary" type="submit" disabled={feedbackSending}>
                {feedbackSending ? 'Enviando…' : <>{Icons.message} Enviar feedback</>}
              </button>
            </form>
          )}
        </div>

        <button type="button" className="btn-secondary" onClick={onLogout}>
          {Icons.logout} Cerrar sesión
        </button>
      </div>
    </div>
  );
}

function BottomTabBar({ currentTab, setTab }) {
  return (
    <div className="bottom-tab-bar tabs-3">
      <div className={`tab-item ${currentTab === 'today' ? 'active' : ''}`} onClick={() => setTab('today')}>
        {Icons.dumbbell}
        <p>Entrenar</p>
      </div>
      <div className={`tab-item ${currentTab === 'routines' ? 'active' : ''}`} onClick={() => setTab('routines')}>
        {Icons.routines}
        <p>Rutinas</p>
      </div>
      <div className={`tab-item ${currentTab === 'settings' ? 'active' : ''}`} onClick={() => setTab('settings')}>
        {Icons.settings}
        <p>Ajustes</p>
      </div>
    </div>
  );
}

// ==========================================
// MAIN APP
// ==========================================
function App() {
  const [theme, setTheme] = useState(() => {
    try {
      const saved = localStorage.getItem('rutinasimple_theme');
      return saved === 'dark' || saved === 'light' ? saved : 'dark';
    } catch {
      return 'dark';
    }
  });
  const [currentScreen, setCurrentScreen] = useState('today'); // 'today' | 'active' | 'routines' | 'settings'
  const [routines, setRoutines] = useState([]);
  const [completedIds, setCompletedIds] = useState([]);
  const [activeRoutine, setActiveRoutine] = useState(null);
  const [user, setUser] = useState(null);
  const [authChecking, setAuthChecking] = useState(true);
  const [editingRoutine, setEditingRoutine] = useState(false);

  const loadCompletions = async (currentUser = user) => {
    if (!currentUser) {
      setCompletedIds([]);
      return;
    }
    try {
      const data = await DataService.getCompletions(currentUser, todayISO());
      setCompletedIds(Array.isArray(data?.routine_ids) ? data.routine_ids : []);
    } catch (err) {
      console.error(err);
      setCompletedIds([]);
    }
  };

  const loadData = async (currentUser = user) => {
    if (!currentUser) {
      setRoutines([]);
      setCompletedIds([]);
      return;
    }
    try {
      const data = await DataService.getAllRoutines(currentUser);
      setRoutines(Array.isArray(data) ? data : []);
      await loadCompletions(currentUser);
    } catch (err) {
      console.error(err);
      if (String(err.message || '').includes('autentic')) {
        AuthService.clearSession();
        setUser(null);
      }
    }
  };

  const refreshSafariChrome = (bg, nextTheme) => {
    // Safari solo relee theme-color si se reemplaza el <meta>
    document.querySelectorAll('meta[name="theme-color"]').forEach((el) => el.remove());
    const themeColor = document.createElement('meta');
    themeColor.setAttribute('name', 'theme-color');
    themeColor.setAttribute('content', bg);
    document.head.appendChild(themeColor);

    let scheme = document.querySelector('meta[name="color-scheme"]');
    if (!scheme) {
      scheme = document.createElement('meta');
      scheme.setAttribute('name', 'color-scheme');
      document.head.appendChild(scheme);
    }
    scheme.setAttribute('content', nextTheme);

    let statusBar = document.querySelector('meta[name="apple-mobile-web-app-status-bar-style"]');
    if (statusBar) {
      statusBar.setAttribute('content', nextTheme === 'dark' ? 'black-translucent' : 'default');
    }

    // Recrear franjas de muestreo con el color nuevo (Liquid Glass / toolbar)
    ['ios-chrome-top', 'ios-chrome-bottom'].forEach((id) => {
      const prev = document.getElementById(id);
      if (!prev) return;
      const next = document.createElement('div');
      next.id = id;
      next.setAttribute('aria-hidden', 'true');
      next.style.backgroundColor = bg;
      prev.replaceWith(next);
    });

    // Segundo pulso: forzar remuestreo tras el paint
    requestAnimationFrame(() => {
      ['ios-chrome-top', 'ios-chrome-bottom'].forEach((id) => {
        const el = document.getElementById(id);
        if (!el) return;
        el.style.backgroundColor = bg;
        el.style.display = 'none';
        void el.offsetHeight;
        el.style.display = '';
      });
    });
  };

  const applyTheme = (nextTheme) => {
    const bg = nextTheme === 'dark' ? '#1A211D' : '#E4EDE7';
    const root = document.documentElement;

    root.setAttribute('data-theme', nextTheme);
    root.style.colorScheme = nextTheme;
    root.style.backgroundColor = bg;
    document.body.style.backgroundColor = bg;

    refreshSafariChrome(bg, nextTheme);
    localStorage.setItem('rutinasimple_theme', nextTheme);
  };

  useEffect(() => {
    const saved = localStorage.getItem('rutinasimple_theme');
    const initial = saved === 'dark' || saved === 'light' ? saved : 'dark';
    setTheme(initial);
    applyTheme(initial);

    (async () => {
      const me = await AuthService.me();
      setUser(me);
      if (me) await loadData(me);
      setAuthChecking(false);
    })();
  }, []);

  const toggleTheme = () => {
    const newTheme = theme === 'light' ? 'dark' : 'light';
    setTheme(newTheme);
    applyTheme(newTheme);
  };

  const handleAuth = async (nextUser) => {
    setUser(nextUser);
    setCurrentScreen('today');
    await loadData(nextUser);
  };

  const handleLogout = async () => {
    await AuthService.logout();
    setUser(null);
    setRoutines([]);
    setCompletedIds([]);
    setActiveRoutine(null);
    setEditingRoutine(false);
    setCurrentScreen('today');
  };

  const handleStartRoutine = (routine) => {
    SoundFx.unlock();
    setActiveRoutine(routine);
    setCurrentScreen('active');
  };

  const handleFinishRoutine = async () => {
    const routine = activeRoutine;
    setCurrentScreen('today');
    setActiveRoutine(null);
    if (routine?.id && user) {
      const id = Number(routine.id);
      // Actualizar UI al instante
      setCompletedIds(prev => (prev.includes(id) ? prev : [...prev, id]));
      try {
        await DataService.completeRoutine(id, user, todayISO());
      } catch (err) {
        console.error(err);
      }
    }
  };

  return (
    <div className="app-container" key={theme}>
      <div className="edge-fade edge-fade-top" aria-hidden="true" />
      <div className="edge-fade edge-fade-bottom" aria-hidden="true" />
      <svg width="0" height="0" style={{position:'absolute'}} aria-hidden="true">
        <defs>
          <linearGradient id="accentGrad" x1="0%" y1="0%" x2="100%" y2="100%">
            <stop offset="0%" stopColor="var(--accent)" />
            <stop offset="100%" stopColor="var(--accent-2)" />
          </linearGradient>
        </defs>
      </svg>

      {authChecking ? (
        <div className="content-area content-full auth-loading">
          <div className="auth-logo-wrap">
            <AppLogo size={56} />
          </div>
          <p>Cargando RutinaSimple...</p>
        </div>
      ) : !user ? (
        <div className="content-area content-full">
          <AuthScreen onAuth={handleAuth} />
        </div>
      ) : (
        <>
          <div className={`content-area ${currentScreen === 'active' || editingRoutine ? 'content-full' : ''}`}>
            {currentScreen === 'today' && (
              <TodayRoutine
                routines={routines}
                completedIds={completedIds}
                onStart={handleStartRoutine}
                onGoToRoutines={() => setCurrentScreen('routines')}
              />
            )}
            {currentScreen === 'active' && (
              <ActiveTracker
                routine={activeRoutine}
                onFinish={handleFinishRoutine}
                onBack={() => setCurrentScreen('today')}
              />
            )}
            {currentScreen === 'routines' && (
              <RoutinesScreen
                routines={routines}
                onDataChange={() => loadData(user)}
                user={user}
                onEditingChange={setEditingRoutine}
              />
            )}
            {currentScreen === 'settings' && (
              <SettingsScreen
                theme={theme}
                toggleTheme={toggleTheme}
                user={user}
                onLogout={handleLogout}
                onUserUpdate={setUser}
              />
            )}
          </div>

          {currentScreen !== 'active' && !editingRoutine && (
            <BottomTabBar
              currentTab={currentScreen}
              setTab={(tab) => {
                setEditingRoutine(false);
                setCurrentScreen(tab);
              }}
            />
          )}
        </>
      )}
    </div>
  );
}

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<App />);
