// Tiny hash-based router. No deps.
// API: <Router routes=[{pattern, component}] fallback />, useRoute(), navigate(path), <Link to>.
// Patterns: '/subject/:subjectId/guide/:guideId' — matched params are passed as component props.

const RouteCtx = React.createContext({ path: '/', params: {} });

function parseHash() {
  const h = window.location.hash;
  return h.startsWith('#') ? (h.slice(1) || '/') : '/';
}

function navigate(path) {
  if (window.location.hash === '#' + path) return;
  window.location.hash = path;
}

function useRoute() {
  return React.useContext(RouteCtx);
}

function Link({ to, children, onClick, style, ...rest }) {
  const handle = (e) => {
    if (e.metaKey || e.ctrlKey || e.shiftKey || e.button === 1) return;
    e.preventDefault();
    navigate(to);
    onClick && onClick(e);
  };
  return (
    <a href={'#' + to} onClick={handle}
       style={{ color: 'inherit', textDecoration: 'none', ...style }} {...rest}>
      {children}
    </a>
  );
}

function matchRoute(pattern, path) {
  if (pattern === '/' && path === '/') return {};
  const pp = pattern.split('/').filter(Boolean);
  const ap = path.split('/').filter(Boolean);
  if (pp.length !== ap.length) return null;
  const params = {};
  for (let i = 0; i < pp.length; i++) {
    if (pp[i].startsWith(':')) {
      params[pp[i].slice(1)] = decodeURIComponent(ap[i]);
    } else if (pp[i] !== ap[i]) {
      return null;
    }
  }
  return params;
}

function Router({ routes, fallback }) {
  const [path, setPath] = React.useState(parseHash());
  React.useEffect(() => {
    const on = () => setPath(parseHash());
    window.addEventListener('hashchange', on);
    if (!window.location.hash) navigate('/');
    return () => window.removeEventListener('hashchange', on);
  }, []);

  let Matched = null;
  let params = {};
  for (const r of routes) {
    const m = matchRoute(r.pattern, path);
    if (m) { Matched = r.component; params = m; break; }
  }
  if (!Matched) Matched = fallback;

  return (
    <RouteCtx.Provider value={{ path, params }}>
      {Matched ? <Matched {...params} /> : null}
    </RouteCtx.Provider>
  );
}

Object.assign(window, { Router, Link, useRoute, navigate });
