/* =====================================================================
 * Composants d'interface réutilisables.
 * ===================================================================== */

const normOpt = (o) => (typeof o === 'object' && o !== null ? o : { value: o, label: o });

const TONE_SOLID = {
  red: 'border-red-600 bg-red-600 text-white',
  emerald: 'border-emerald-600 bg-emerald-600 text-white',
  amber: 'border-amber-500 bg-amber-500 text-white',
  blue: 'border-blue-600 bg-blue-600 text-white',
  violet: 'border-violet-600 bg-violet-600 text-white',
  slate: 'border-slate-700 bg-slate-700 text-white',
};

const TONE_SOFT = {
  slate: 'bg-slate-100 text-slate-700 ring-slate-200',
  red: 'bg-red-50 text-red-700 ring-red-200',
  amber: 'bg-amber-50 text-amber-800 ring-amber-200',
  emerald: 'bg-emerald-50 text-emerald-700 ring-emerald-200',
  blue: 'bg-blue-50 text-blue-700 ring-blue-200',
  violet: 'bg-violet-50 text-violet-700 ring-violet-200',
};

/* ---------- Boutons ---------- */

function Button({ variant = 'primary', size = 'md', icon: IconCmp, children, className, ...props }) {
  const variants = {
    primary: 'bg-red-600 text-white shadow-sm hover:bg-red-700',
    secondary: 'border border-slate-300 bg-white text-slate-700 shadow-sm hover:bg-slate-50',
    ghost: 'text-slate-600 hover:bg-slate-100',
    danger: 'border border-red-200 bg-white text-red-600 hover:bg-red-50',
    dark: 'bg-slate-800 text-white shadow-sm hover:bg-slate-900',
    success: 'bg-emerald-600 text-white shadow-sm hover:bg-emerald-700',
  };
  const sizes = {
    sm: 'px-2.5 py-1.5 text-xs',
    md: 'px-3.5 py-2 text-sm',
    lg: 'px-5 py-3 text-base',
  };
  return (
    <button
      type="button"
      className={cx(
        'inline-flex items-center justify-center gap-2 rounded-lg font-medium transition active:scale-[0.98] disabled:cursor-not-allowed disabled:opacity-50',
        variants[variant],
        sizes[size],
        className,
      )}
      {...props}
    >
      {IconCmp && <IconCmp className={size === 'sm' ? 'h-3.5 w-3.5' : 'h-4 w-4'} />}
      {children}
    </button>
  );
}

function IconButton({ icon: IconCmp, title, tone = 'slate', className, ...props }) {
  const tones = {
    slate: 'text-slate-500 hover:bg-slate-100 hover:text-slate-800',
    red: 'text-red-500 hover:bg-red-50 hover:text-red-700',
    blue: 'text-blue-600 hover:bg-blue-50',
  };
  return (
    <button type="button" title={title} aria-label={title} className={cx('rounded-lg p-2 transition', tones[tone], className)} {...props}>
      <IconCmp className="h-4 w-4" />
    </button>
  );
}

/* ---------- Conteneurs ---------- */

function Card({ title, subtitle, icon: IconCmp, leading, actions, children, className, bodyClassName }) {
  return (
    <section className={cx('min-w-0 rounded-xl border border-slate-200 bg-white shadow-sm', className)}>
      {(title || actions) && (
        <header className="flex flex-wrap items-center justify-between gap-3 border-b border-slate-100 px-4 py-3">
          <div className="flex min-w-0 items-center gap-3">
            {leading}
            {IconCmp && (
              <div className="flex h-9 w-9 shrink-0 items-center justify-center rounded-lg bg-red-50 text-red-600">
                <IconCmp className="h-5 w-5" />
              </div>
            )}
            <div className="min-w-0">
              <h3 className="truncate font-semibold text-slate-800">{title}</h3>
              {subtitle && <p className="text-xs text-slate-500">{subtitle}</p>}
            </div>
          </div>
          {actions && <div className="flex flex-wrap items-center gap-2">{actions}</div>}
        </header>
      )}
      <div className={cx('p-4', bodyClassName)}>{children}</div>
    </section>
  );
}

function PageHeader({ title, subtitle, icon: IconCmp, actions }) {
  return (
    <div className="mb-6 flex flex-wrap items-center justify-between gap-4">
      <div className="flex items-center gap-3">
        {IconCmp && (
          <div className="flex h-11 w-11 items-center justify-center rounded-xl bg-red-600 text-white shadow-sm shadow-red-600/30">
            <IconCmp className="h-6 w-6" />
          </div>
        )}
        <div>
          <h1 className="text-xl font-bold text-slate-900 sm:text-2xl">{title}</h1>
          {subtitle && <p className="text-sm text-slate-500">{subtitle}</p>}
        </div>
      </div>
      {actions && <div className="flex flex-wrap items-center gap-2">{actions}</div>}
    </div>
  );
}

function StatCard({ label, value, icon: IconCmp, tone = 'slate', hint, onClick }) {
  const tones = {
    slate: 'bg-slate-100 text-slate-600',
    red: 'bg-red-100 text-red-600',
    amber: 'bg-amber-100 text-amber-600',
    emerald: 'bg-emerald-100 text-emerald-600',
    blue: 'bg-blue-100 text-blue-600',
    violet: 'bg-violet-100 text-violet-600',
  };
  const Tag = onClick ? 'button' : 'div';
  return (
    <Tag
      type={onClick ? 'button' : undefined}
      onClick={onClick}
      className={cx(
        'flex w-full min-w-0 items-center gap-3 rounded-xl border border-slate-200 bg-white p-3 text-left shadow-sm sm:gap-4 sm:p-4',
        onClick && 'transition hover:-translate-y-0.5 hover:shadow-md',
      )}
    >
      <div className={cx('flex h-10 w-10 shrink-0 items-center justify-center rounded-xl sm:h-12 sm:w-12', tones[tone])}>
        <IconCmp className="h-5 w-5 sm:h-6 sm:w-6" />
      </div>
      <div className="min-w-0">
        <p className="text-xl font-bold text-slate-900 sm:text-2xl">{value}</p>
        <p className="text-xs leading-tight text-slate-500 sm:truncate sm:text-sm">{label}</p>
        {hint && <p className="truncate text-xs text-slate-400">{hint}</p>}
      </div>
    </Tag>
  );
}

function EmptyState({ icon: IconCmp = Info, title, children, action }) {
  return (
    <div className="flex flex-col items-center justify-center rounded-xl border-2 border-dashed border-slate-200 px-6 py-12 text-center">
      <IconCmp className="mb-3 h-10 w-10 text-slate-300" />
      <p className="font-semibold text-slate-700">{title}</p>
      {children && <p className="mt-1 max-w-md text-sm text-slate-500">{children}</p>}
      {action && <div className="mt-4">{action}</div>}
    </div>
  );
}

function Badge({ tone = 'slate', children, className }) {
  return (
    <span className={cx('inline-flex items-center gap-1 whitespace-nowrap rounded-full px-2 py-0.5 text-xs font-semibold ring-1 ring-inset', TONE_SOFT[tone], className)}>
      {children}
    </span>
  );
}

/* ---------- Champs de formulaire ---------- */

function Field({ label, hint, children, className, extra }) {
  return (
    <div className={cx('block', className)}>
      {(label || extra) && (
        <div className="mb-1.5 flex items-center justify-between gap-2">
          {label && <span className="text-xs font-semibold uppercase tracking-wide text-slate-500">{label}</span>}
          {extra}
        </div>
      )}
      {children}
      {hint && <span className="mt-1 block text-xs text-slate-400">{hint}</span>}
    </div>
  );
}

function TextInput({ value, onChange, className, ...props }) {
  return <input className={cx('input', className)} value={value ?? ''} onChange={(e) => onChange(e.target.value)} {...props} />;
}

function TextArea({ value, onChange, className, rows = 3, ...props }) {
  return (
    <textarea rows={rows} className={cx('input resize-y', className)} value={value ?? ''} onChange={(e) => onChange(e.target.value)} {...props} />
  );
}

const TONE_INPUT = {
  danger: 'border-red-500 bg-red-50 text-red-700 focus:border-red-500',
  warning: 'border-amber-400 bg-amber-50 text-amber-800 focus:border-amber-500',
  ok: 'border-emerald-400 bg-emerald-50/40 text-emerald-800',
};

function NumberInput({ value, onChange, unit, tone, className, ...props }) {
  return (
    <div className="relative">
      <input
        type="number"
        inputMode="decimal"
        className={cx('input text-lg font-semibold', unit && 'pr-16', tone && TONE_INPUT[tone], className)}
        value={value ?? ''}
        onChange={(e) => onChange(e.target.value)}
        {...props}
      />
      {unit && <span className="pointer-events-none absolute inset-y-0 right-3 flex items-center text-xs font-medium text-slate-400">{unit}</span>}
    </div>
  );
}

function SelectInput({ value, onChange, options, placeholder = '— Sélectionner —', className }) {
  return (
    <select className={cx('input', className)} value={value ?? ''} onChange={(e) => onChange(e.target.value)}>
      {placeholder !== null && <option value="">{placeholder}</option>}
      {options.map((o) => {
        const opt = normOpt(o);
        return (
          <option key={opt.value} value={opt.value}>
            {opt.label}
          </option>
        );
      })}
    </select>
  );
}

/** Heure HH:MM avec bouton « maintenant ». */
function TimeInput({ value, onChange }) {
  return (
    <div className="flex gap-2">
      <input type="time" className="input" value={value ?? ''} onChange={(e) => onChange(e.target.value)} />
      <Button variant="secondary" icon={Clock} onClick={() => onChange(nowTime())} title="Heure actuelle">
        <span className="hidden sm:inline">Maint.</span>
      </Button>
    </div>
  );
}

/** Sélection multiple sous forme de pastilles. */
function ChipGroup({ options, value, onChange, tone = 'red' }) {
  const arr = Array.isArray(value) ? value : [];
  const toggle = (v) => onChange(arr.includes(v) ? arr.filter((x) => x !== v) : [...arr, v]);
  return (
    <div className="flex flex-wrap gap-2">
      {options.map((o) => {
        const opt = normOpt(o);
        const active = arr.includes(opt.value);
        return (
          <button key={opt.value} type="button" onClick={() => toggle(opt.value)} className={cx('chip', active ? TONE_SOLID[opt.tone || tone] : 'chip-off')}>
            {active && <Check className="h-3.5 w-3.5" />}
            {opt.label}
          </button>
        );
      })}
    </div>
  );
}

/** Choix unique (cliquer de nouveau désélectionne). */
function Segmented({ options, value, onChange, tone = 'red', allowEmpty = true, size = 'md', className }) {
  return (
    <div className={cx('flex flex-wrap gap-2', className)}>
      {options.map((o) => {
        const opt = normOpt(o);
        const active = value === opt.value;
        return (
          <button
            key={opt.value}
            type="button"
            onClick={() => onChange(active && allowEmpty ? '' : opt.value)}
            className={cx(
              'rounded-lg border font-medium transition active:scale-95',
              size === 'lg' ? 'px-4 py-3 text-sm' : 'px-3 py-2 text-sm',
              active ? TONE_SOLID[opt.tone || tone] + ' shadow-sm' : 'border-slate-300 bg-white text-slate-600 hover:bg-slate-50',
            )}
          >
            {opt.label}
          </button>
        );
      })}
    </div>
  );
}

/** Oui / Non. `danger` indique quelle réponse est péjorative. */
function YesNo({ value, onChange, danger = 'oui' }) {
  return (
    <Segmented
      value={value}
      onChange={onChange}
      options={[
        { value: 'oui', label: 'Oui', tone: danger === 'oui' ? 'red' : 'emerald' },
        { value: 'non', label: 'Non', tone: danger === 'non' ? 'red' : 'emerald' },
      ]}
    />
  );
}

function Switch({ checked, onChange, disabled, label }) {
  return (
    <button
      type="button"
      role="switch"
      aria-checked={Boolean(checked)}
      disabled={disabled}
      onClick={() => onChange(!checked)}
      className={cx('inline-flex items-center gap-2 disabled:cursor-not-allowed disabled:opacity-50')}
    >
      <span className={cx('relative inline-flex h-6 w-11 shrink-0 rounded-full transition', checked ? 'bg-emerald-500' : 'bg-slate-300')}>
        <span className={cx('absolute top-0.5 h-5 w-5 rounded-full bg-white shadow transition-all', checked ? 'left-[22px]' : 'left-0.5')} />
      </span>
      {label && <span className="text-sm text-slate-700">{label}</span>}
    </button>
  );
}

function Checkbox({ checked, onChange, label, className }) {
  return (
    <label className={cx('inline-flex cursor-pointer select-none items-center gap-2 text-sm text-slate-700', className)}>
      <input type="checkbox" className="h-4 w-4 rounded border-slate-300 accent-red-600" checked={Boolean(checked)} onChange={(e) => onChange(e.target.checked)} />
      {label}
    </label>
  );
}

function SearchInput({ value, onChange, placeholder = 'Rechercher…', className }) {
  return (
    <div className={cx('relative', className)}>
      <Search className="pointer-events-none absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-slate-400" />
      <input className="input pl-9" value={value} placeholder={placeholder} onChange={(e) => onChange(e.target.value)} />
    </div>
  );
}

/* ---------- Fenêtre modale ---------- */

function Modal({ open, onClose, title, subtitle, icon: IconCmp, children, footer, size = 'lg' }) {
  useEffect(() => {
    if (!open) return undefined;
    const onKey = (e) => e.key === 'Escape' && onClose && onClose();
    window.addEventListener('keydown', onKey);
    document.body.style.overflow = 'hidden';
    return () => {
      window.removeEventListener('keydown', onKey);
      document.body.style.overflow = '';
    };
  }, [open, onClose]);

  if (!open) return null;
  const sizes = { sm: 'max-w-md', md: 'max-w-2xl', lg: 'max-w-4xl', xl: 'max-w-6xl' };
  return (
    <div
      className="fixed inset-0 z-50 flex items-start justify-center overflow-y-auto bg-slate-900/50 p-3 backdrop-blur-sm sm:p-8"
      onMouseDown={(e) => e.target === e.currentTarget && onClose && onClose()}
    >
      <div className={cx('flex max-h-[calc(100dvh-1.5rem)] w-full flex-col rounded-2xl bg-white shadow-2xl sm:max-h-[calc(100dvh-4rem)]', sizes[size])}>
        <header className="flex items-center justify-between gap-3 border-b border-slate-100 px-5 py-4">
          <div className="flex items-center gap-3">
            {IconCmp && (
              <div className="flex h-9 w-9 items-center justify-center rounded-lg bg-red-50 text-red-600">
                <IconCmp className="h-5 w-5" />
              </div>
            )}
            <div>
              <h2 className="font-semibold text-slate-900">{title}</h2>
              {subtitle && <p className="text-xs text-slate-500">{subtitle}</p>}
            </div>
          </div>
          {onClose && <IconButton icon={X} title="Fermer" onClick={onClose} />}
        </header>
        <div className="flex-1 overflow-y-auto p-5">{children}</div>
        {footer && <footer className="flex flex-wrap justify-end gap-2 rounded-b-2xl border-t border-slate-100 bg-slate-50 px-5 py-3">{footer}</footer>}
      </div>
    </div>
  );
}

/* ---------- Notifications ---------- */

function Toasts() {
  const { toasts } = useApp();
  return (
    <div className="pointer-events-none fixed bottom-4 left-4 right-4 z-[60] flex flex-col items-end gap-2 sm:left-auto">
      {toasts.map((t) => (
        <div
          key={t.id}
          className={cx(
            'toast-enter flex items-center gap-2 rounded-lg px-4 py-3 text-sm font-medium text-white shadow-lg',
            t.tone === 'red' ? 'bg-red-600' : t.tone === 'amber' ? 'bg-amber-500' : 'bg-slate-800',
          )}
        >
          {t.tone === 'red' ? <AlertTriangle className="h-4 w-4" /> : <CheckCircle2 className="h-4 w-4 text-emerald-400" />}
          {t.message}
        </div>
      ))}
    </div>
  );
}

function AccessDenied({ module }) {
  return (
    <div className="mx-auto mt-16 max-w-lg">
      <EmptyState icon={LockIcon} title="Accès non autorisé">
        Votre compte n'a pas accès au module « {module} ». Contactez un administrateur depuis « Gestion accès ».
      </EmptyState>
    </div>
  );
}
