/* global React, ReactDOM */
const { useState, useEffect, useMemo, useRef, useCallback } = React;

// ---------- helpers ----------
const PLANS = {
  mensual: { label: "Mensual", days: 30, short: "MES" },
  quincenal: { label: "Quincenal", days: 15, short: "QNL" },
  semanal: { label: "Semanal", days: 7, short: "SEM" },
};

const STATUSES = {
  pagado: { label: "Pagado", color: "green" },
  pendiente: { label: "Pendiente", color: "amber" },
  vencido: { label: "Vencido", color: "red" },
};

const AVATAR_COLORS = [
  "#1e3a8a", "#831843", "#365314", "#7c2d12", "#3730a3",
  "#134e4a", "#581c87", "#92400e", "#155e75", "#9f1239",
];

const todayISO = () => new Date().toISOString().slice(0, 10);
const addDaysISO = (iso, days) => {
  const d = new Date(iso + "T00:00:00");
  d.setDate(d.getDate() + days);
  return d.toISOString().slice(0, 10);
};
const daysBetween = (a, b) => {
  const ms = new Date(b + "T00:00:00") - new Date(a + "T00:00:00");
  return Math.round(ms / (1000 * 60 * 60 * 24));
};

const fmtDate = (iso) => {
  if (!iso) return "—";
  const d = new Date(iso + "T00:00:00");
  return d.toLocaleDateString("es-MX", { day: "2-digit", month: "short", year: "numeric" });
};

const fmtRel = (iso) => {
  if (!iso) return "";
  const days = daysBetween(todayISO(), iso);
  if (days === 0) return "hoy";
  if (days === 1) return "mañana";
  if (days === -1) return "ayer";
  if (days > 0) return `en ${days}d`;
  return `hace ${Math.abs(days)}d`;
};

const initialsOf = (name) =>
  name.trim().split(/\s+/).slice(0, 2).map((p) => p[0] || "").join("").toUpperCase() || "?";

const avatarColor = (name) => {
  let hash = 0;
  for (let i = 0; i < name.length; i++) hash = (hash * 31 + name.charCodeAt(i)) | 0;
  return AVATAR_COLORS[Math.abs(hash) % AVATAR_COLORS.length];
};

const deriveStatus = (m) => {
  if (m.manual_status) return m.status;
  const days = daysBetween(todayISO(), m.due);
  if (days < 0) return "vencido";
  if (days <= 3) return "pendiente";
  return "pagado";
};

// ---------- API ----------
const api = {
  getAll: () => fetch("/api/members").then((r) => r.json()),
  create: (data) =>
    fetch("/api/members", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify(data),
    }).then((r) => r.json()),
  update: (id, patch) =>
    fetch(`/api/members/${id}`, {
      method: "PUT",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify(patch),
    }).then((r) => r.json()),
  remove: (id) =>
    fetch(`/api/members/${id}`, { method: "DELETE" }).then((r) => r.json()),
};

// ---------- icons ----------
const Icon = ({ d, size = 16, fill = "none" }) => (
  <svg width={size} height={size} viewBox="0 0 24 24" fill={fill} stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">{d}</svg>
);
const IconSearch = (p) => <Icon {...p} d={<><circle cx="11" cy="11" r="7"/><path d="m20 20-3.5-3.5"/></>} />;
const IconPlus = (p) => <Icon {...p} d={<><path d="M12 5v14M5 12h14"/></>} />;
const IconCheck = (p) => <Icon {...p} d={<path d="M20 6 9 17l-5-5"/>} />;
const IconRefresh = (p) => <Icon {...p} d={<><path d="M21 12a9 9 0 0 1-15.5 6.3L3 16"/><path d="M3 8a9 9 0 0 1 15.5-6.3L21 4"/><path d="M21 2v4h-4"/><path d="M3 22v-4h4"/></>} />;
const IconTrash = (p) => <Icon {...p} d={<><path d="M3 6h18"/><path d="M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/><path d="m6 6 1 14a2 2 0 0 0 2 2h6a2 2 0 0 0 2-2l1-14"/></>} />;
const IconX = (p) => <Icon {...p} d={<><path d="M18 6 6 18M6 6l12 12"/></>} />;

// ---------- editable cell ----------
function EditableCell({ value, onSave, type = "text", placeholder, mono = false }) {
  const [editing, setEditing] = useState(false);
  const [val, setVal] = useState(value || "");
  const inputRef = useRef(null);

  useEffect(() => { setVal(value || ""); }, [value]);
  useEffect(() => {
    if (editing && inputRef.current) {
      inputRef.current.focus();
      if (type === "text") inputRef.current.select();
    }
  }, [editing]);

  const commit = () => {
    setEditing(false);
    if (val !== value) onSave(val);
  };
  const cancel = () => {
    setVal(value || "");
    setEditing(false);
  };

  if (editing) {
    return (
      <input
        ref={inputRef}
        className="cell-input"
        type={type}
        value={val}
        onChange={(e) => setVal(e.target.value)}
        onBlur={commit}
        onKeyDown={(e) => {
          if (e.key === "Enter") commit();
          if (e.key === "Escape") cancel();
        }}
      />
    );
  }

  return (
    <div
      className={`cell ${!value ? "empty" : ""}`}
      style={mono ? { fontFamily: "var(--mono)", fontSize: 12 } : null}
      onClick={() => setEditing(true)}
      tabIndex={0}
      onKeyDown={(e) => { if (e.key === "Enter") setEditing(true); }}
    >
      {type === "date" ? (
        value ? (
          <>
            <div className="date-mono">{fmtDate(value)}</div>
            <div className="date-rel">{fmtRel(value)}</div>
          </>
        ) : <span>—</span>
      ) : (value || placeholder || "—")}
    </div>
  );
}

// ---------- main app ----------
function App() {
  const [members, setMembers] = useState([]);
  const [loading, setLoading] = useState(true);
  const [filter, setFilter] = useState("todos");
  const [statusFilter, setStatusFilter] = useState(null);
  const [search, setSearch] = useState("");
  const [showAdd, setShowAdd] = useState(false);

  useEffect(() => {
    api.getAll().then((data) => {
      // normalize mysql TINYINT → bool
      setMembers(data.map((m) => ({ ...m, manual_status: !!m.manual_status })));
      setLoading(false);
    });
  }, []);

  const update = useCallback((id, patch) => {
    setMembers((ms) => ms.map((m) => m.id === id ? { ...m, ...patch } : m));
    api.update(id, patch);
  }, []);

  const remove = useCallback((id) => {
    if (!confirm("¿Eliminar este miembro?")) return;
    setMembers((ms) => ms.filter((m) => m.id !== id));
    api.remove(id);
  }, []);

  const markPaid = useCallback((id) => {
    setMembers((ms) => ms.map((m) => {
      if (m.id !== id) return m;
      const start = todayISO();
      const due = addDaysISO(start, PLANS[m.plan].days);
      const patch = { start, due, status: "pagado", manual_status: true };
      api.update(id, patch);
      return { ...m, ...patch };
    }));
  }, []);

  const renew = useCallback((id) => {
    setMembers((ms) => ms.map((m) => {
      if (m.id !== id) return m;
      const base = m.due || todayISO();
      const due = addDaysISO(base, PLANS[m.plan].days);
      const patch = { due, status: "pagado", manual_status: false };
      api.update(id, patch);
      return { ...m, ...patch };
    }));
  }, []);

  const addMember = useCallback((data) => {
    const id = `m_${Date.now()}_${Math.random().toString(36).slice(2, 6)}`;
    const start = data.start || todayISO();
    const due = data.due || addDaysISO(start, PLANS[data.plan].days);
    const member = { id, name: data.name, plan: data.plan, start, due, status: "pagado", manual_status: false };
    setMembers((ms) => [member, ...ms]);
    api.create(member);
  }, []);

  const enriched = useMemo(() => members.map((m) => ({ ...m, _status: deriveStatus(m) })), [members]);

  const stats = useMemo(() => {
    const s = { total: enriched.length, pagado: 0, pendiente: 0, vencido: 0 };
    enriched.forEach((m) => { s[m._status] = (s[m._status] || 0) + 1; });
    return s;
  }, [enriched]);

  const filtered = useMemo(() => {
    const q = search.trim().toLowerCase();
    return enriched.filter((m) => {
      if (filter !== "todos" && m.plan !== filter) return false;
      if (statusFilter && m._status !== statusFilter) return false;
      if (q && !m.name.toLowerCase().includes(q)) return false;
      return true;
    });
  }, [enriched, filter, statusFilter, search]);

  return (
    <div className="app">
      <header className="topbar">
        <div className="brand">
          <div className="brand-mark">E</div>
          <div>
            <div className="brand-name">Enniovalegym</div>
            <div className="brand-sub">Gestor de miembros</div>
          </div>
        </div>
        <div className="topbar-right">
          <div className="today">{new Date().toLocaleDateString("es-MX", { weekday: "short", day: "2-digit", month: "short", year: "numeric" }).toUpperCase()}</div>
          <button className="btn btn-primary" onClick={() => setShowAdd(true)}>
            <IconPlus size={14} /> Agregar miembro
          </button>
        </div>
      </header>

      <section className="stats">
        <StatCard label="Total miembros" value={stats.total} dot="" active={!statusFilter} onClick={() => setStatusFilter(null)} foot="Todos los planes" valueColor="#fafafa" />
        <StatCard label="Pagados" value={stats.pagado} dot="green" active={statusFilter === "pagado"} onClick={() => setStatusFilter(statusFilter === "pagado" ? null : "pagado")} foot="Al corriente" valueColor="var(--accent-fg)" />
        <StatCard label="Pendientes" value={stats.pendiente} dot="amber" tone="warn" active={statusFilter === "pendiente"} onClick={() => setStatusFilter(statusFilter === "pendiente" ? null : "pendiente")} foot="Vencen pronto" valueColor="var(--warn-fg)" />
        <StatCard label="Vencidos" value={stats.vencido} dot="red" tone="danger" active={statusFilter === "vencido"} onClick={() => setStatusFilter(statusFilter === "vencido" ? null : "vencido")} foot="Requieren renovación" valueColor="var(--danger-fg)" />
      </section>

      <div className="toolbar">
        <div className="search">
          <span className="search-icon"><IconSearch size={15} /></span>
          <input
            type="text"
            placeholder="Buscar por nombre…"
            value={search}
            onChange={(e) => setSearch(e.target.value)}
          />
        </div>
        <div className="chips" role="tablist">
          {[
            ["todos", "Todos"],
            ["mensual", "Mensual"],
            ["quincenal", "Quincenal"],
            ["semanal", "Semanal"],
          ].map(([k, l]) => (
            <button key={k} className={`chip ${filter === k ? "active" : ""}`} onClick={() => setFilter(k)}>{l}</button>
          ))}
        </div>
      </div>

      <div className="table-wrap">
        <div className="table-meta">
          <span>{filtered.length} de {members.length} miembros</span>
          <span>Click cualquier celda para editar</span>
        </div>
        <div className="table-scroll">
          {loading ? (
            <div className="loading-state">Cargando miembros…</div>
          ) : (
            <table className="grid">
              <thead>
                <tr>
                  <th className="col-row">#</th>
                  <th className="col-name">Nombre</th>
                  <th className="col-plan">Plan</th>
                  <th className="col-date">Inicio</th>
                  <th className="col-date">Vencimiento</th>
                  <th className="col-status">Estado</th>
                  <th className="col-actions">Acciones</th>
                </tr>
              </thead>
              <tbody>
                {filtered.length === 0 ? (
                  <tr>
                    <td colSpan="7">
                      <div className="empty-state">
                        <div className="empty-state-title">Sin resultados</div>
                        <div className="empty-state-sub">No hay miembros que coincidan con el filtro</div>
                      </div>
                    </td>
                  </tr>
                ) : filtered.map((m, idx) => (
                  <Row
                    key={m.id}
                    index={idx + 1}
                    member={m}
                    onUpdate={update}
                    onRemove={remove}
                    onPay={markPaid}
                    onRenew={renew}
                  />
                ))}
              </tbody>
            </table>
          )}
        </div>
        <div className="footer-hint">
          <span><kbd>Click</kbd> editar celda</span>
          <span><kbd>Enter</kbd> guardar</span>
          <span><kbd>Esc</kbd> cancelar</span>
        </div>
      </div>

      {showAdd && <AddModal onClose={() => setShowAdd(false)} onAdd={addMember} />}
    </div>
  );
}

function StatCard({ label, value, dot, foot, active, tone, onClick, valueColor }) {
  return (
    <button className={`stat ${active ? "active" : ""} ${tone || ""}`} onClick={onClick}>
      <div className="stat-label">
        {dot ? <span className={`stat-dot ${dot}`}></span> : <span className="stat-dot"></span>}
        {label}
      </div>
      <div className="stat-value" style={{ color: valueColor }}>{value}</div>
      <div className="stat-foot">{foot}</div>
    </button>
  );
}

function Row({ index, member, onUpdate, onRemove, onPay, onRenew }) {
  const status = member._status;
  const planInfo = PLANS[member.plan];

  return (
    <tr>
      <td className="col-row"><div className="row-num">{String(index).padStart(2, "0")}</div></td>
      <td className="col-name">
        <div className="name-cell">
          <div className="avatar" style={{ background: avatarColor(member.name) }}>{initialsOf(member.name)}</div>
          <EditableCell value={member.name} onSave={(v) => onUpdate(member.id, { name: v || "Sin nombre" })} />
        </div>
      </td>
      <td className="col-plan">
        <select
          className="select-cell"
          value={member.plan}
          onChange={(e) => {
            const newPlan = e.target.value;
            const due = addDaysISO(member.start, PLANS[newPlan].days);
            onUpdate(member.id, { plan: newPlan, due, manual_status: false });
          }}
        >
          {Object.entries(PLANS).map(([k, v]) => (
            <option key={k} value={k}>{v.label}</option>
          ))}
        </select>
      </td>
      <td className="col-date">
        <EditableCell
          value={member.start}
          type="date"
          onSave={(v) => {
            const due = addDaysISO(v, PLANS[member.plan].days);
            onUpdate(member.id, { start: v, due, manual_status: false });
          }}
        />
      </td>
      <td className="col-date">
        <EditableCell
          value={member.due}
          type="date"
          onSave={(v) => onUpdate(member.id, { due: v, manual_status: false })}
        />
      </td>
      <td className="col-status">
        <StatusCell
          status={status}
          onChange={(v) => onUpdate(member.id, { status: v, manual_status: true })}
        />
      </td>
      <td className="col-actions">
        <div className="row-actions">
          <button className="icon-btn pay" title="Marcar como pagado y renovar" onClick={() => onPay(member.id)}>
            <IconCheck size={14} />
          </button>
          <button className="icon-btn renew" title={`Renovar +${planInfo.days}d`} onClick={() => onRenew(member.id)}>
            <IconRefresh size={14} />
          </button>
          <button className="icon-btn del" title="Eliminar" onClick={() => onRemove(member.id)}>
            <IconTrash size={14} />
          </button>
        </div>
      </td>
    </tr>
  );
}

function StatusCell({ status, onChange }) {
  const info = STATUSES[status];
  return (
    <div style={{ position: "relative", padding: "8px 14px" }}>
      <span className={`badge ${info.color}`} style={{ pointerEvents: "none" }}>
        <span className="dot"></span>{info.label}
      </span>
      <select
        value={status}
        onChange={(e) => onChange(e.target.value)}
        style={{
          position: "absolute", inset: 0,
          width: "100%", height: "100%",
          opacity: 0, cursor: "pointer",
          background: "transparent", border: "none",
          appearance: "none",
        }}
      >
        {Object.entries(STATUSES).map(([k, v]) => (
          <option key={k} value={k}>{v.label}</option>
        ))}
      </select>
    </div>
  );
}

function AddModal({ onClose, onAdd }) {
  const [name, setName] = useState("");
  const [plan, setPlan] = useState("mensual");
  const [start, setStart] = useState(todayISO());
  const due = useMemo(() => addDaysISO(start, PLANS[plan].days), [start, plan]);

  const submit = () => {
    if (!name.trim()) return;
    onAdd({ name: name.trim(), plan, start, due });
    onClose();
  };

  return (
    <div className="modal-back" onClick={onClose}>
      <div className="modal" onClick={(e) => e.stopPropagation()}>
        <div className="modal-head">
          <div className="modal-title">Nuevo miembro</div>
          <button className="btn-ghost" onClick={onClose}><IconX size={16} /></button>
        </div>
        <div className="modal-body">
          <div className="field">
            <label>Nombre completo</label>
            <input
              autoFocus
              type="text"
              placeholder="Ej. María García"
              value={name}
              onChange={(e) => setName(e.target.value)}
              onKeyDown={(e) => { if (e.key === "Enter") submit(); }}
            />
          </div>
          <div className="field">
            <label>Plan</label>
            <select value={plan} onChange={(e) => setPlan(e.target.value)}>
              {Object.entries(PLANS).map(([k, v]) => (
                <option key={k} value={k}>{v.label} ({v.days} días)</option>
              ))}
            </select>
          </div>
          <div className="field-row">
            <div className="field">
              <label>Fecha de inicio</label>
              <input type="date" value={start} onChange={(e) => setStart(e.target.value)} />
            </div>
            <div className="field">
              <label>Vencimiento (auto)</label>
              <input type="date" value={due} disabled />
            </div>
          </div>
        </div>
        <div className="modal-foot">
          <button className="btn" onClick={onClose}>Cancelar</button>
          <button className="btn btn-primary" onClick={submit} disabled={!name.trim()}>
            <IconPlus size={14} /> Agregar
          </button>
        </div>
      </div>
    </div>
  );
}

ReactDOM.createRoot(document.getElementById("root")).render(<App />);
