// ROCA GOLF — API client, Toast, Modal, exportCSV

const API_PREFIX = 'api/';

// ── Core fetch ─────────────────────────────────────────────────────────────
async function apiFetch(path, method = 'GET', body = null, params = {}) {
  let url = API_PREFIX + path;
  const qs = Object.entries(params)
    .filter(([, v]) => v != null && v !== '')
    .map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(v)}`)
    .join('&');
  if (qs) url += (url.includes('?') ? '&' : '?') + qs;

  const token = sessionStorage.getItem('rg_token');
  const headers = { 'Content-Type': 'application/json' };
  if (token) headers['Authorization'] = 'Bearer ' + token;

  const opts = { method, headers };
  if (body && method !== 'GET') opts.body = JSON.stringify(body);

  const res = await fetch(url, opts);
  if (res.status === 401) {
    sessionStorage.clear();
    window.location.reload();
    throw new Error('Sesión expirada');
  }
  const json = await res.json();
  if (!json.ok) throw new Error(json.error || 'Error del servidor');
  return json.data;
}

// ── Data hook ──────────────────────────────────────────────────────────────
function useApiData(path, params = {}, skip = false) {
  const [data,    setData]    = useState(null);
  const [loading, setLoading] = useState(!skip);
  const [error,   setError]   = useState(null);
  const [rev,     setRev]     = useState(0);
  const paramsKey = JSON.stringify(params);

  useEffect(() => {
    if (skip) { setLoading(false); return; }
    let cancelled = false;
    setLoading(true);
    apiFetch(path, 'GET', null, params)
      .then(d  => { if (!cancelled) { setData(d); setLoading(false); setError(null); } })
      .catch(e => { if (!cancelled) { setError(e.message); setLoading(false); } });
    return () => { cancelled = true; };
  }, [path, paramsKey, rev, skip]);

  return { data, loading, error, reload: () => setRev(r => r + 1) };
}

// ── Debounce hook ──────────────────────────────────────────────────────────
function useDebounce(value, delay = 420) {
  const [debounced, setDebounced] = useState(value);
  useEffect(() => {
    const t = setTimeout(() => setDebounced(value), delay);
    return () => clearTimeout(t);
  }, [value, delay]);
  return debounced;
}

// ── Toast ──────────────────────────────────────────────────────────────────
let _toastId = 0;
let _toasts  = [];
const _toastSubs = new Set();
function _notifyToast() { _toastSubs.forEach(fn => fn([..._toasts])); }

function toast(msg, type = 'success') {
  const id = ++_toastId;
  _toasts = [..._toasts, { id, msg, type }];
  _notifyToast();
  setTimeout(() => { _toasts = _toasts.filter(t => t.id !== id); _notifyToast(); }, 4500);
}
toast.error   = msg => toast(msg, 'error');
toast.success = msg => toast(msg, 'success');
toast.info    = msg => toast(msg, 'info');

function ToastContainer() {
  const [list, setList] = useState([]);
  useEffect(() => {
    _toastSubs.add(setList);
    return () => _toastSubs.delete(setList);
  }, []);
  if (!list.length) return null;
  return (
    <div className="rg-toast-stack">
      {list.map(t => (
        <div key={t.id} className={`rg-toast ${t.type}`}>
          {t.type === 'error'
            ? <svg width="14" height="14" viewBox="0 0 14 14" fill="none"><circle cx="7" cy="7" r="6" stroke="currentColor" strokeWidth="1.5"/><path d="M7 4v3M7 9.5v.5" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round"/></svg>
            : <svg width="14" height="14" viewBox="0 0 14 14" fill="none"><path d="M2.5 7l3 3 6-6" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round"/></svg>
          }
          <span>{t.msg}</span>
        </div>
      ))}
    </div>
  );
}

// ── Modal base ─────────────────────────────────────────────────────────────
function RgModal({ open, onClose, title, width = 560, children, footer }) {
  useEffect(() => {
    if (!open) return;
    const h = e => { if (e.key === 'Escape') onClose(); };
    window.addEventListener('keydown', h);
    return () => window.removeEventListener('keydown', h);
  }, [open, onClose]);

  if (!open) return null;
  return (
    <div className="rg-overlay" onMouseDown={e => e.target === e.currentTarget && onClose()}>
      <div className="rg-dialog" style={{ maxWidth: width }}>
        <div className="rg-dialog-head">
          <h3 className="rg-dialog-title">{title}</h3>
          <button className="rg-dialog-close" onClick={onClose}>✕</button>
        </div>
        <div className="rg-dialog-body">{children}</div>
        {footer && <div className="rg-dialog-foot">{footer}</div>}
      </div>
    </div>
  );
}

// Skeleton loader row
function SkeletonRows({ cols = 6, rows = 8 }) {
  return Array.from({ length: rows }, (_, i) => (
    <tr key={i}>
      {Array.from({ length: cols }, (_, j) => (
        <td key={j}>
          <div style={{ height: 13, borderRadius: 4, background: 'var(--rg-line)', width: j === 0 ? '60%' : '80%', animation: 'rg-pulse 1.3s ease-in-out infinite' }}/>
        </td>
      ))}
    </tr>
  ));
}

// ── CSV export ─────────────────────────────────────────────────────────────
function exportCSV(rows, cols, filename) {
  const header = cols.map(c => `"${c.label}"`).join(',');
  const body = rows.map(r =>
    cols.map(c => {
      const v = typeof c.key === 'function' ? c.key(r) : (r[c.key] ?? '');
      return `"${String(v).replace(/"/g, '""')}"`;
    }).join(',')
  ).join('\n');
  const blob = new Blob(['﻿' + header + '\n' + body], { type: 'text/csv;charset=utf-8;' });
  const url = URL.createObjectURL(blob);
  const a = document.createElement('a');
  a.href = url; a.download = filename; a.click();
  URL.revokeObjectURL(url);
}

// Skeleton pulse keyframe (injected once)
(function injectSkeleton() {
  if (document.getElementById('rg-skeleton-style')) return;
  const s = document.createElement('style');
  s.id = 'rg-skeleton-style';
  s.textContent = '@keyframes rg-pulse { 0%,100%{opacity:1}50%{opacity:.45} }';
  document.head.appendChild(s);
})();

Object.assign(window, { apiFetch, useApiData, useDebounce, toast, ToastContainer, RgModal, exportCSV, SkeletonRows });
