// UI primitives — buttons, inputs, badges, modals, etc.

const { useState, useEffect, useRef, useMemo, useCallback, createContext, useContext } = React;

/* ---------- Error Boundary ----------
   Note: assigned to `var` (and window) so it's accessible from other text/babel scripts.
   `class X { }` in script scope is NOT visible across separate <script> tags. */
var ErrorBoundary = window.ErrorBoundary = class extends React.Component {
  constructor(props) {
    super(props);
    this.state = { error: null, info: null };
    this.reset = this.reset.bind(this);
  }
  static getDerivedStateFromError(error) {
    return { error };
  }
  componentDidCatch(error, info) {
    this.setState({ info });
    console.error('[ErrorBoundary]', error, info);
  }
  reset() {
    this.setState({ error: null, info: null });
    if (this.props.onReset) this.props.onReset();
  }
  render() {
    if (this.state.error) {
      const err = this.state.error;
      const stack = (this.state.info && this.state.info.componentStack) || (err && err.stack) || '';
      return (
        <div style={{ padding: 32, maxWidth: 900, margin: '40px auto', fontFamily: 'var(--font-sans)' }}>
          <div style={{ background: '#FEE2E2', border: '1px solid #FCA5A5', borderRadius: 14, padding: 24 }}>
            <div style={{ fontSize: 22, fontWeight: 700, color: '#991B1B', marginBottom: 8 }}>
              Sahifada xato yuz berdi
            </div>
            <div style={{ fontSize: 14, color: '#7F1D1D', marginBottom: 12 }}>
              {String(err && (err.message || err))}
            </div>
            <details style={{ marginTop: 12, fontSize: 12, color: '#7F1D1D' }}>
              <summary style={{ cursor: 'pointer', fontWeight: 600 }}>Texnik tafsilot</summary>
              <pre style={{ marginTop: 8, fontSize: 11, whiteSpace: 'pre-wrap', fontFamily: 'monospace' }}>{stack}</pre>
            </details>
            <button onClick={this.reset} style={{ marginTop: 16, padding: '8px 16px', background: '#991B1B', color: '#fff', border: 'none', borderRadius: 8, fontSize: 14, fontWeight: 600, cursor: 'pointer' }}>
              Qayta urinish
            </button>
          </div>
        </div>
      );
    }
    return this.props.children;
  }
};

/* ---------- Icon ---------- */
function Icon({ name, weight, className = '', style }) {
  const cls = weight === 'fill' ? 'ph-fill' : weight === 'bold' ? 'ph-bold' : 'ph';
  return <i className={`${cls} ph-${name} ${className}`} style={style} aria-hidden="true"></i>;
}

/* ---------- Brand mark ---------- */
function LogoMark({ size = 32 }) {
  return (
    <svg width={size} height={size} viewBox="0 0 32 32" fill="none">
      <defs>
        <linearGradient id={`ysr-${size}`} x1="0" y1="0" x2="32" y2="32" gradientUnits="userSpaceOnUse">
          <stop offset="0" stopColor="#0F172A"></stop>
          <stop offset="0.6" stopColor="#1E40AF"></stop>
          <stop offset="1" stopColor="#38BDF8"></stop>
        </linearGradient>
      </defs>
      <rect x="0" y="0" width="32" height="32" rx="9" fill={`url(#ysr-${size})`}></rect>
      <path d="M16 7 C16 7, 19 9, 19 12 C19 15, 16 17, 16 17" stroke="rgba(255,255,255,0.4)" strokeWidth="1.2" strokeLinecap="round" fill="none"/>
      <path d="M16 7 C16 7, 22 9, 22 13 C22 17, 16 19, 16 19" stroke="rgba(255,255,255,0.22)" strokeWidth="1.2" strokeLinecap="round" fill="none"/>
      <path d="M12.5 11.5 C11 11.5, 10 12.6, 10 14.2 c0 1, 0.25 2, 0.5 3 c0.5 1.8, 0.8 3.6, 1.1 5.2 c0.2 1.1, 0.7 1.7, 1.4 1.7 c0.6 0, 1.0 -0.4, 1.3 -1.4 c0.3 -1.1, 0.5 -2.1, 0.7 -2.9 c0.15 -0.55, 0.4 -0.85, 0.7 -0.85 c0.3 0, 0.55 0.3, 0.7 0.85 c0.2 0.8, 0.4 1.8, 0.7 2.9 c0.3 1, 0.7 1.4, 1.3 1.4 c0.7 0, 1.2 -0.6, 1.4 -1.7 c0.3 -1.6, 0.6 -3.4, 1.1 -5.2 c0.25 -1, 0.5 -2, 0.5 -3 c0 -1.6, -1.0 -2.7, -2.5 -2.7 c-0.8 0, -1.5 0.3, -2.0 0.7 c-0.35 0.3, -0.85 0.55, -1.3 0.55 c-0.45 0, -0.95 -0.25, -1.3 -0.55 c-0.5 -0.4, -1.2 -0.7, -2.0 -0.7 z" fill="white"/>
    </svg>
  );
}

function BrandLockup({ size = 32 }) {
  return (
    <div className="row row-3" style={{ gap: 10 }}>
      <LogoMark size={size} />
      <div style={{ lineHeight: 1.1 }}>
        <div style={{ fontSize: 15, fontWeight: 700, letterSpacing: '-0.01em', color: 'var(--fg-1)' }}>
          Your <span style={{ color: 'var(--brand-secondary)' }}>Smile</span>
        </div>
        <div style={{ fontSize: 10, fontWeight: 600, letterSpacing: '0.16em', color: 'var(--fg-3)', textTransform: 'uppercase', marginTop: 2 }}>
          Rentgen
        </div>
      </div>
    </div>
  );
}

/* ---------- Button ---------- */
function Button({ variant = 'secondary', size, icon, iconRight, loading, children, className = '', ...props }) {
  const cls = ['btn', `btn--${variant}`, size && `btn--${size}`, className].filter(Boolean).join(' ');
  return (
    <button className={cls} {...props}>
      {loading ? <Icon name="circle-notch" className="spin" /> : (icon && <Icon name={icon} />)}
      {children}
      {iconRight && <Icon name={iconRight} />}
    </button>
  );
}

/* ---------- Field ---------- */
function Field({ label, hint, error, icon, children, ...props }) {
  return (
    <div className={`field ${icon ? 'field--icon' : ''}`} {...props}>
      {label && <label className="field__label">{label}</label>}
      {children}
      {icon && <Icon name={icon} className="field__icon" />}
      {hint && !error && <div className="field__hint">{hint}</div>}
      {error && <div className="field__error">{error}</div>}
    </div>
  );
}

function Input({ icon, ...props }) {
  return <input className={`input ${icon ? 'input--with-icon' : ''}`} {...props} />;
}

function Select({ children, ...props }) {
  return <select className="select" {...props}>{children}</select>;
}

/* ---------- Badge ---------- */
function Badge({ tone = 'neutral', icon, children, nodot, className = '' }) {
  const cls = `badge badge--${tone} ${nodot ? 'badge--no-dot' : ''} ${className}`;
  return (
    <span className={cls}>
      {icon && <Icon name={icon} style={{ fontSize: 12 }} />}
      {children}
    </span>
  );
}

function StatusBadge({ status }) {
  const map = {
    paid:    { tone: 'success', label: "To'landi" },
    partial: { tone: 'warning', label: 'Qisman' },
    unpaid:  { tone: 'danger',  label: 'Qarz' },
    ready:   { tone: 'success', label: 'Tayyor' },
    pending: { tone: 'warning', label: 'Kutilmoqda' },
    uploaded:{ tone: 'info',    label: 'Yuklandi' },
    active:  { tone: 'success', label: 'Faol' },
    inactive:{ tone: 'neutral', label: 'Nofaol' },
  };
  const m = map[status] || { tone: 'neutral', label: status };
  return <Badge tone={m.tone}>{m.label}</Badge>;
}

function PayTypeBadge({ type }) {
  const map = {
    cash:     { icon: 'money',          label: 'Naqd' },
    card:     { icon: 'credit-card',    label: 'Karta' },
    transfer: { icon: 'arrows-left-right', label: "O'tkazma" },
    debt:     { icon: 'clock-counter-clockwise', label: 'Qarz' },
  };
  const m = map[type] || { icon: 'wallet', label: type };
  return (
    <span style={{ display: 'inline-flex', alignItems: 'center', gap: 6, fontSize: 12, color: 'var(--fg-2)' }}>
      <Icon name={m.icon} style={{ fontSize: 14 }} />
      {m.label}
    </span>
  );
}

/* ---------- Avatar ---------- */
function Avatar({ name, size = '', neutral = false }) {
  const initials = name ? name.split(' ').slice(0,2).map(s => s[0]).join('').toUpperCase() : '?';
  const cls = ['avatar', size && `avatar--${size}`, neutral && 'avatar--neutral'].filter(Boolean).join(' ');
  return <div className={cls}>{initials}</div>;
}

/* ---------- Card ---------- */
function Card({ children, hover, glass, className = '', style, ...props }) {
  const cls = ['card', hover && 'card--hover', glass && 'card--glass', className].filter(Boolean).join(' ');
  return <div className={cls} style={style} {...props}>{children}</div>;
}

/* ---------- StatCard (animated counter) ---------- */
function useAnimatedNumber(target, duration = 900) {
  const [val, setVal] = useState(0);
  useEffect(() => {
    let start;
    let raf;
    const startVal = 0;
    const step = (ts) => {
      if (!start) start = ts;
      const p = Math.min(1, (ts - start) / duration);
      const eased = 1 - Math.pow(1 - p, 3);
      setVal(startVal + (target - startVal) * eased);
      if (p < 1) raf = requestAnimationFrame(step);
    };
    raf = requestAnimationFrame(step);
    return () => raf && cancelAnimationFrame(raf);
  }, [target, duration]);
  return val;
}

function StatCard({ icon, iconTone, label, value, format, delta, deltaLabel, sparkline }) {
  const animated = useAnimatedNumber(value || 0);
  const displayed = format ? format(animated) : Math.round(animated);
  return (
    <div className="stat-card">
      <div className="stat-card__top">
        <span className="stat-card__label">{label}</span>
        <div className={`stat-card__icon ${iconTone ? `stat-card__icon--${iconTone}` : ''}`}>
          <Icon name={icon} />
        </div>
      </div>
      <div className="stat-card__value kpi-value">{displayed}</div>
      {delta != null && (
        <div className={`stat-card__delta stat-card__delta--${delta >= 0 ? 'up' : 'down'}`}>
          <Icon name={delta >= 0 ? 'arrow-up' : 'arrow-down'} style={{ fontSize: 12 }} />
          {delta >= 0 ? '+' : ''}{delta}%
          <span className="stat-card__delta-period">{deltaLabel || "kechaga nisbatan"}</span>
        </div>
      )}
      {sparkline && (
        <svg className="stat-card__spark" viewBox="0 0 200 36" preserveAspectRatio="none">
          <defs>
            <linearGradient id={`sp-${label.replace(/\s/g,'')}`} x1="0" x2="0" y1="0" y2="1">
              <stop offset="0" stopColor="var(--brand-secondary)" stopOpacity="0.3"/>
              <stop offset="1" stopColor="var(--brand-secondary)" stopOpacity="0"/>
            </linearGradient>
          </defs>
          <path d={sparkline.area} fill={`url(#sp-${label.replace(/\s/g,'')})`} />
          <path d={sparkline.line} fill="none" stroke="var(--brand-secondary)" strokeWidth="1.5" />
        </svg>
      )}
    </div>
  );
}

function makeSpark(points) {
  // points: array of numbers (any length)
  const max = Math.max(...points, 1);
  const min = Math.min(...points, 0);
  const range = max - min || 1;
  const w = 200, h = 36;
  const stepX = w / (points.length - 1);
  const coords = points.map((v, i) => [i * stepX, h - 4 - ((v - min) / range) * (h - 8)]);
  const line = 'M' + coords.map(c => `${c[0].toFixed(1)} ${c[1].toFixed(1)}`).join(' L ');
  const area = line + ` L ${w} ${h} L 0 ${h} Z`;
  return { line, area };
}

/* ---------- Modal ---------- */
function Modal({ open, onClose, title, size, children, footer }) {
  useEffect(() => {
    if (!open) return;
    const h = (e) => { if (e.key === 'Escape') onClose(); };
    document.addEventListener('keydown', h);
    return () => document.removeEventListener('keydown', h);
  }, [open, onClose]);
  if (!open) return null;
  return (
    <div className="modal-scrim" onClick={onClose}>
      <div className={`modal ${size === 'lg' ? 'modal--lg' : ''}`} onClick={e => e.stopPropagation()}>
        <div className="modal__header">
          <h3 className="modal__title">{title}</h3>
          <button className="modal__close" onClick={onClose} aria-label="Yopish"><Icon name="x" /></button>
        </div>
        <div className="modal__body">{children}</div>
        {footer && <div className="modal__footer">{footer}</div>}
      </div>
    </div>
  );
}

/* ---------- Toast ---------- */
const ToastContext = createContext(null);
function useToast() { return useContext(ToastContext); }

function ToastProvider({ children }) {
  const [toasts, setToasts] = useState([]);
  const push = useCallback((t) => {
    const id = Math.random().toString(36).slice(2);
    setToasts(prev => [...prev, { id, ...t }]);
    setTimeout(() => setToasts(prev => prev.filter(x => x.id !== id)), t.duration || 3200);
  }, []);
  return (
    <ToastContext.Provider value={push}>
      {children}
      <div className="toast-container">
        {toasts.map(t => (
          <div key={t.id} className={`toast toast--${t.kind || 'info'}`}>
            <div className="toast__icon"><Icon name={t.icon || (t.kind === 'success' ? 'check-circle' : t.kind === 'warning' ? 'warning' : t.kind === 'danger' ? 'x-circle' : 'info')} /></div>
            <div className="toast__body">
              <div className="toast__title">{t.title}</div>
              {t.msg && <div className="toast__msg">{t.msg}</div>}
            </div>
          </div>
        ))}
      </div>
    </ToastContext.Provider>
  );
}

/* ---------- Combobox (searchable select) ---------- */
function Combobox({ placeholder, items, value, onChange, displayKey = 'name', secondaryKey, renderItem, onCreate, createLabel }) {
  const [q, setQ] = useState('');
  const [open, setOpen] = useState(false);
  const wrapRef = useRef(null);

  useEffect(() => {
    const h = (e) => { if (wrapRef.current && !wrapRef.current.contains(e.target)) setOpen(false); };
    document.addEventListener('mousedown', h);
    return () => document.removeEventListener('mousedown', h);
  }, []);

  const filtered = useMemo(() => {
    const s = q.trim().toLowerCase();
    if (!s) return items;
    return items.filter(it => {
      const hay = [it[displayKey], it[secondaryKey], it.phone].filter(Boolean).join(' ').toLowerCase();
      return hay.includes(s);
    });
  }, [q, items, displayKey, secondaryKey]);

  return (
    <div className="combobox field--icon" ref={wrapRef}>
      <Icon name="magnifying-glass" className="field__icon" style={{ bottom: 9 }} />
      <input
        className="input input--with-icon"
        placeholder={placeholder}
        value={q}
        onChange={(e) => { setQ(e.target.value); setOpen(true); }}
        onFocus={() => setOpen(true)}
      />
      {open && (
        <div className="combobox__list">
          {filtered.slice(0, 10).map(it => (
            <div key={it.id} className="combobox__item" onClick={() => { onChange(it); setOpen(false); setQ(''); }}>
              {renderItem ? renderItem(it, q) : (
                <>
                  <Avatar name={it[displayKey]} size="sm" neutral />
                  <div style={{ flex: 1, minWidth: 0 }}>
                    <div style={{ fontWeight: 500, fontSize: 13 }}>{highlight(it[displayKey], q)}</div>
                    {secondaryKey && it[secondaryKey] && <div style={{ fontSize: 11, color: 'var(--fg-3)' }}>{highlight(it[secondaryKey], q)}</div>}
                  </div>
                </>
              )}
            </div>
          ))}
          {filtered.length === 0 && (
            <div className="combobox__empty">
              <div style={{ marginBottom: 10 }}>Hech narsa topilmadi</div>
              {onCreate && q && (
                <Button variant="primary" size="sm" icon="plus" onClick={() => { onCreate(q); setOpen(false); setQ(''); }}>
                  {createLabel || `"${q}" qo'shish`}
                </Button>
              )}
            </div>
          )}
          {onCreate && filtered.length > 0 && (
            <>
              <div style={{ height: 1, background: 'var(--border-2)', margin: '4px 0' }}></div>
              <div className="combobox__item" onClick={() => { onCreate(q); setOpen(false); setQ(''); }} style={{ color: 'var(--brand-primary)' }}>
                <Icon name="plus" /><span>{createLabel || 'Yangi qo\u2019shish'}</span>
              </div>
            </>
          )}
        </div>
      )}
    </div>
  );
}

function highlight(str, q) {
  if (!q || !str) return str;
  const s = String(str);
  const idx = s.toLowerCase().indexOf(q.toLowerCase());
  if (idx < 0) return s;
  return (
    <>
      {s.slice(0, idx)}
      <mark className="hl">{s.slice(idx, idx + q.length)}</mark>
      {s.slice(idx + q.length)}
    </>
  );
}

/* ---------- Tabs ---------- */
function Tabs({ items, value, onChange }) {
  return (
    <div className="tabs">
      {items.map(it => (
        <button key={it.id} className={`tab ${value === it.id ? 'is-active' : ''}`} onClick={() => onChange(it.id)}>
          {it.icon && <Icon name={it.icon} style={{ fontSize: 16, marginRight: 6, verticalAlign: 'text-bottom' }} />}
          {it.label}
          {it.count != null && <span style={{ marginLeft: 6, fontSize: 11, color: 'var(--fg-4)' }}>{it.count}</span>}
        </button>
      ))}
    </div>
  );
}

/* ---------- Chips (filter pills) ---------- */
function Chips({ items, value, onChange, multi }) {
  const isActive = (id) => multi ? value.includes(id) : value === id;
  const toggle = (id) => {
    if (multi) {
      onChange(value.includes(id) ? value.filter(v => v !== id) : [...value, id]);
    } else {
      onChange(id);
    }
  };
  return (
    <div className="chips">
      {items.map(it => (
        <button key={it.id} className={`chip ${isActive(it.id) ? 'is-active' : ''}`} onClick={() => toggle(it.id)}>
          {it.label}
          {it.count != null && <span className="chip__count">{it.count}</span>}
        </button>
      ))}
    </div>
  );
}

/* ---------- Pagination ---------- */
function Pagination({ page, total, pageSize, onPage }) {
  const pages = Math.max(1, Math.ceil(total / pageSize));
  const items = [];
  for (let i = 1; i <= pages; i++) {
    if (i === 1 || i === pages || Math.abs(i - page) <= 1) {
      items.push(i);
    } else if (items[items.length - 1] !== '…') {
      items.push('…');
    }
  }
  const start = total === 0 ? 0 : (page - 1) * pageSize + 1;
  const end = Math.min(total, page * pageSize);
  return (
    <div className="pagination">
      <button className="pagination__page" disabled={page === 1} onClick={() => onPage(page - 1)}><Icon name="caret-left" /></button>
      {items.map((p, i) => p === '…'
        ? <span key={i} style={{ padding: '0 4px', color: 'var(--fg-4)' }}>…</span>
        : <button key={i} className={`pagination__page ${p === page ? 'is-active' : ''}`} onClick={() => onPage(p)}>{p}</button>
      )}
      <button className="pagination__page" disabled={page === pages} onClick={() => onPage(page + 1)}><Icon name="caret-right" /></button>
      <span className="pagination__info">{start}–{end} / {total}</span>
    </div>
  );
}

/* ---------- Empty state ---------- */
function EmptyState({ art = 'patients', title, sub, action }) {
  const arts = {
    patients: (
      <svg viewBox="0 0 120 120" fill="none" className="empty__art">
        <circle cx="60" cy="60" r="56" fill="var(--brand-primary-soft)" />
        <circle cx="60" cy="52" r="18" stroke="var(--brand-primary)" strokeWidth="2" />
        <path d="M30 92 C30 78, 42 70, 60 70 C78 70, 90 78, 90 92" stroke="var(--brand-primary)" strokeWidth="2" fill="none" />
      </svg>
    ),
    search: (
      <svg viewBox="0 0 120 120" fill="none" className="empty__art">
        <circle cx="60" cy="60" r="56" fill="var(--brand-primary-soft)" />
        <circle cx="54" cy="54" r="20" stroke="var(--brand-primary)" strokeWidth="2.5" fill="none"/>
        <line x1="69" y1="69" x2="84" y2="84" stroke="var(--brand-primary)" strokeWidth="2.5" strokeLinecap="round"/>
      </svg>
    ),
    inbox: (
      <svg viewBox="0 0 120 120" fill="none" className="empty__art">
        <circle cx="60" cy="60" r="56" fill="var(--brand-primary-soft)" />
        <rect x="36" y="46" width="48" height="36" rx="4" stroke="var(--brand-primary)" strokeWidth="2" fill="none"/>
        <path d="M36 56 L60 70 L84 56" stroke="var(--brand-primary)" strokeWidth="2" fill="none"/>
      </svg>
    ),
    debt: (
      <svg viewBox="0 0 120 120" fill="none" className="empty__art">
        <circle cx="60" cy="60" r="56" fill="var(--color-success-soft)" />
        <circle cx="60" cy="60" r="22" stroke="var(--color-success)" strokeWidth="2" fill="none"/>
        <path d="M52 60 L58 66 L70 54" stroke="var(--color-success)" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round" fill="none"/>
      </svg>
    ),
    xray: (
      <svg viewBox="0 0 120 120" fill="none" className="empty__art">
        <circle cx="60" cy="60" r="56" fill="var(--brand-primary-soft)" />
        <rect x="40" y="40" width="40" height="50" rx="4" stroke="var(--brand-primary)" strokeWidth="2" fill="none"/>
        <path d="M48 56 C48 51, 51 48, 55 48 C57 48, 58.5 49, 60 50 C61.5 49, 63 48, 65 48 C69 48, 72 51, 72 56 C72 64, 68 72, 66 78 C65 81, 63 81, 62 78 L60.5 72 C60.2 71, 59.8 71, 59.5 72 L58 78 C57 81, 55 81, 54 78 C52 72, 48 64, 48 56 Z" fill="var(--brand-primary)" opacity="0.7"/>
      </svg>
    ),
  };
  return (
    <div className="empty">
      {arts[art] || arts.patients}
      <div className="empty__title">{title}</div>
      {sub && <p className="empty__sub">{sub}</p>}
      {action}
    </div>
  );
}

/* ---------- Bar chart ---------- */
function BarChart({ data, max, valueFormat, height = 200 }) {
  const cap = max || Math.max(...data.map(d => d.value));
  return (
    <div className="bars" style={{ height }}>
      {data.map((d, i) => (
        <div key={i} className="bars__col">
          <div className="bars__bar" style={{ height: `${(d.value / cap) * 100}%` }} title={valueFormat ? valueFormat(d.value) : d.value}></div>
          <div className="bars__label">{d.day || d.label}</div>
        </div>
      ))}
    </div>
  );
}

/* ---------- Helper: format groups ---------- */
function groupServicesByCat(services) {
  const out = {};
  for (const s of services) (out[s.cat] ||= []).push(s);
  return out;
}

Object.assign(window, {
  ErrorBoundary,
  Icon, LogoMark, BrandLockup, Button, Field, Input, Select, Badge, StatusBadge, PayTypeBadge,
  Avatar, Card, StatCard, makeSpark, Modal, ToastProvider, useToast, Combobox, highlight,
  Tabs, Chips, Pagination, EmptyState, BarChart, useAnimatedNumber, groupServicesByCat,
});
