// Workspace — shared primitives: actors, avatars, badges, buttons, icons

// Legacy seeded actors. Multi-tenant users (post Fase 1+) get registered
// dynamically from data.jsx (loadMe + loadWorkspaceMembers) by writing to
// `ACTORS[user.id] = ...`. Anything keyed by a UUID we never registered
// returns a safe default through the Proxy below — that way components
// reading `ACTORS[someUserId].name` never crash if the lookup misses.
const _ACTORS_DATA = {
  founder:  { id: 'founder',  name: 'Sebastián', role: 'Founder',          color: 'var(--founder)', initial: 'S', desc: 'Visión, prioridades, decisiones de producto' },
  tlead:    { id: 'tlead',    name: 'Genaro',    role: 'Technical Lead',   color: 'var(--tlead)',   initial: 'G', desc: 'Validación técnica, revisión de código' },
  code:     { id: 'code',     name: 'Claude Code', role: 'Code Agent',     color: 'var(--code)',    initial: 'C', desc: 'Implementación autónoma de código' },
  claude:   { id: 'claude',   name: 'Claude',    role: 'Strategic Advisor',color: 'var(--claude)',  initial: 'A', desc: 'Asesoría estratégica, generación de contenido' },
};

// Build a default actor entry for an unknown id. The id might be a UUID of a
// user we haven't loaded yet, an actor row that was deleted, or null/undef.
// Returning a safe shape (with `.name`, `.color`, etc.) prevents the whole
// app from crashing when a stale assigneeId or authorId is rendered.
function _defaultActor(id) {
  const safeId = (id == null || id === '') ? '?' : String(id);
  // Short, readable fallback name. Use the first segment of a UUID or the
  // raw id if it's already short enough. The real name gets filled when
  // loadWorkspaceMembers runs.
  const shortName = safeId.length > 12 ? safeId.slice(0, 8) : safeId;
  return {
    id: safeId,
    name: shortName,
    role: '',
    color: 'var(--text-2)',
    initial: (shortName[0] || '?').toUpperCase(),
    desc: '',
    _isFallback: true,
  };
}

const ACTORS = new Proxy(_ACTORS_DATA, {
  get(target, prop) {
    if (prop in target) return target[prop];
    // Symbol props (e.g. Symbol.iterator) — let them pass through untouched
    // so spreading / for..of doesn't accidentally hit our fallback.
    if (typeof prop === 'symbol') return undefined;
    return _defaultActor(prop);
  },
  // Allow components to register/update entries (used by data.jsx after
  // /api/me + /api/workspace/members succeed).
  set(target, prop, value) {
    target[prop] = value;
    return true;
  },
  has(target, prop) {
    // `has` is kept honest so `prop in ACTORS` still returns false for
    // unknown ids — only direct property access falls through to the
    // default. Code that wants to test "is this a real actor?" can use
    // `prop in ACTORS` instead of truthy-check on `.name`.
    return prop in target;
  },
});
const ACTOR_LIST = ['founder', 'tlead', 'code', 'claude'];

// ── Lucide icon wrapper ─────────────────────────────────────────────
// Uses lucide global from CDN. Returns an inline SVG.
function Icon({ name, size = 16, color, strokeWidth = 1.75, style, className }) {
  const ref = React.useRef(null);
  React.useEffect(() => {
    if (!window.lucide || !ref.current || !name) return;
    const node = window.lucide.icons?.[toPascal(name)] || window.lucide.icons?.[name];
    if (!node) return;
    ref.current.innerHTML = '';
    const svg = window.lucide.createElement(node);
    svg.setAttribute('width', size);
    svg.setAttribute('height', size);
    svg.setAttribute('stroke-width', strokeWidth);
    if (color) svg.setAttribute('stroke', color);
    ref.current.appendChild(svg);
  }, [name, size, color, strokeWidth]);
  return <span ref={ref} className={className} style={{ display: 'inline-flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0, ...style }} />;
}
function toPascal(s) {
  if (!s || typeof s !== 'string') return '';
  return s.split(/[-_]/).map(w => (w[0] ? w[0].toUpperCase() + w.slice(1) : '')).join('');
}

// ── Avatar ──────────────────────────────────────────────────────────
// Fallback chain for the initial: explicit `initial` field on the actor
// catalog entry → first letter of `name` → first letter of `desc` (which
// holds the email for User-derived actors) → "?". Stops the avatar from
// ever rendering "?" for a user we *do* know the name of just because the
// catalog entry was populated before the initial was computed.
function _avatarInitial(a) {
  const candidates = [a?.initial, a?.name, a?.desc, a?.id];
  for (const c of candidates) {
    const s = String(c || '').trim();
    if (!s) continue;
    const ch = s.charAt(0).toUpperCase();
    if (ch && ch !== '?') return ch;
  }
  return '?';
}

function Avatar({ actor, size = 24, ring = false, style }) {
  const a = ACTORS[actor] || ACTORS.founder;
  return (
    <div title={a.name} style={{
      width: size, height: size, borderRadius: '50%',
      background: a.color, color: '#0F1115',
      display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
      fontWeight: 600, fontSize: Math.round(size * 0.42),
      flexShrink: 0,
      boxShadow: ring ? `0 0 0 2px var(--bg-0), 0 0 0 3.5px ${a.color}` : 'none',
      ...style,
    }}>{_avatarInitial(a)}</div>
  );
}

function AvatarStack({ actors, size = 22 }) {
  return (
    <div style={{ display: 'inline-flex' }}>
      {actors.map((a, i) => (
        <div key={i} style={{ marginLeft: i === 0 ? 0 : -6, border: '2px solid var(--bg-1)', borderRadius: '50%' }}>
          <Avatar actor={a} size={size} />
        </div>
      ))}
    </div>
  );
}

// ── Buttons ─────────────────────────────────────────────────────────
function Button({ variant = 'primary', size = 'md', icon, iconRight, children, onClick, disabled, type = 'button', style }) {
  const sizes = {
    sm: { h: 26, px: 10, fs: 12, gap: 6 },
    md: { h: 32, px: 14, fs: 13, gap: 7 },
    lg: { h: 38, px: 18, fs: 14, gap: 8 },
  };
  const s = sizes[size];
  const variants = {
    primary: { bg: 'var(--accent)', col: '#0A0E1A', bgH: 'var(--accent-hover)', bd: 'transparent' },
    secondary: { bg: 'var(--bg-2)', col: 'var(--text-0)', bgH: 'var(--bg-3)', bd: 'var(--border)' },
    ghost: { bg: 'transparent', col: 'var(--text-1)', bgH: 'var(--bg-2)', bd: 'transparent' },
    danger: { bg: 'transparent', col: 'var(--danger)', bgH: 'rgba(239,68,68,.10)', bd: 'rgba(239,68,68,.25)' },
    dangerSolid: { bg: 'var(--danger)', col: '#fff', bgH: '#DC2626', bd: 'transparent' },
  };
  const v = variants[variant] || variants.primary;
  const [hover, setHover] = React.useState(false);
  return (
    <button type={type} onClick={onClick} disabled={disabled}
      onMouseEnter={() => setHover(true)} onMouseLeave={() => setHover(false)}
      style={{
        display: 'inline-flex', alignItems: 'center', justifyContent: 'center', gap: s.gap,
        height: s.h, padding: `0 ${s.px}px`,
        background: hover && !disabled ? v.bgH : v.bg,
        color: v.col,
        border: `1px solid ${v.bd}`,
        borderRadius: 'var(--r-md)',
        fontSize: s.fs, fontWeight: 500,
        cursor: disabled ? 'not-allowed' : 'pointer',
        opacity: disabled ? 0.5 : 1,
        transition: 'background var(--t-fast), border-color var(--t-fast)',
        whiteSpace: 'nowrap',
        ...style,
      }}>
      {icon && <Icon name={icon} size={s.fs + 2} />}
      {children}
      {iconRight && <Icon name={iconRight} size={s.fs + 2} />}
    </button>
  );
}

// ── Badge / Chip ────────────────────────────────────────────────────
function Badge({ children, color, bg, dot, mono, style }) {
  return (
    <span style={{
      display: 'inline-flex', alignItems: 'center', gap: 5,
      padding: '2px 8px', height: 20,
      background: bg || 'var(--bg-2)',
      color: color || 'var(--text-1)',
      border: '1px solid var(--border)',
      borderRadius: 999,
      fontSize: 11, fontWeight: 500,
      fontFamily: mono ? 'var(--font-mono)' : 'inherit',
      whiteSpace: 'nowrap',
      ...style,
    }}>
      {dot && <span style={{ width: 6, height: 6, borderRadius: '50%', background: color || 'var(--text-1)' }} />}
      {children}
    </span>
  );
}

const PRIORITY_META = {
  P0: { color: '#EF4444', bg: 'rgba(239,68,68,.10)', border: 'rgba(239,68,68,.25)' },
  P1: { color: '#F59E0B', bg: 'rgba(245,158,11,.10)', border: 'rgba(245,158,11,.22)' },
  P2: { color: '#4F8EFF', bg: 'rgba(79,142,255,.10)', border: 'rgba(79,142,255,.25)' },
  P3: { color: '#6B7185', bg: 'rgba(107,113,133,.12)', border: 'rgba(107,113,133,.25)' },
};
function PriorityChip({ p }) {
  const m = PRIORITY_META[p] || PRIORITY_META.P3;
  return (
    <span className="mono" style={{
      display: 'inline-flex', alignItems: 'center', height: 18, padding: '0 6px',
      background: m.bg, color: m.color, border: `1px solid ${m.border}`,
      borderRadius: 4, fontSize: 10.5, fontWeight: 600, letterSpacing: 0.3,
    }}>{p}</span>
  );
}

const STATUS_META = {
  backlog:   { label: 'BACKLOG',    color: 'var(--st-backlog)',   bg: 'rgba(107,113,133,.10)' },
  ready:     { label: 'LISTO',      color: 'var(--st-ready)',     bg: 'rgba(96,165,250,.12)' },
  progress:  { label: 'EN CURSO',   color: 'var(--st-progress)',  bg: 'rgba(79,142,255,.12)' },
  review:    { label: 'REVISIÓN',   color: 'var(--st-review)',    bg: 'rgba(245,158,11,.12)' },
  blocked:   { label: 'BLOQUEADO',  color: 'var(--st-blocked)',   bg: 'rgba(239,68,68,.12)' },
  closed:    { label: 'CERRADO',    color: 'var(--st-closed)',    bg: 'rgba(16,185,129,.12)' },
  discarded: { label: 'DESCARTADA', color: 'var(--st-discarded)', bg: 'rgba(120,120,120,.10)' },
};

function StatusDot({ s, size = 8 }) {
  const m = STATUS_META[s] || STATUS_META.backlog;
  return <span style={{ width: size, height: size, borderRadius: '50%', background: m.color, flexShrink: 0 }} />;
}

// ── Tag (phase, label) ──────────────────────────────────────────────
function Tag({ children, color, style, mono }) {
  return (
    <span className={mono ? 'mono' : ''} style={{
      display: 'inline-flex', alignItems: 'center', height: 18, padding: '0 6px',
      background: 'var(--bg-2)', color: color || 'var(--text-1)',
      border: '1px solid var(--border)',
      borderRadius: 4, fontSize: 11, fontWeight: 500,
      ...style,
    }}>{children}</span>
  );
}

// ── Toast ───────────────────────────────────────────────────────────
function Toast({ kind = 'info', title, body, onClose }) {
  const meta = {
    success: { color: 'var(--success)', icon: 'check-circle-2' },
    error:   { color: 'var(--danger)',  icon: 'alert-circle' },
    info:    { color: 'var(--info)',    icon: 'info' },
    warn:    { color: 'var(--warn)',    icon: 'alert-triangle' },
  }[kind];
  return (
    <div style={{
      display: 'flex', alignItems: 'flex-start', gap: 10,
      width: 320, padding: 12,
      background: 'var(--bg-2)',
      border: '1px solid var(--border)',
      borderLeft: `2px solid ${meta.color}`,
      borderRadius: 'var(--r-md)',
      boxShadow: '0 8px 24px rgba(0,0,0,.4)',
      animation: 'slide-in-right 200ms ease-out',
    }}>
      <div style={{ color: meta.color, marginTop: 1 }}><Icon name={meta.icon} size={16} /></div>
      <div style={{ flex: 1, minWidth: 0 }}>
        <div style={{ fontSize: 13, fontWeight: 600, color: 'var(--text-0)' }}>{title}</div>
        {body && <div style={{ fontSize: 12, color: 'var(--text-1)', marginTop: 2 }}>{body}</div>}
      </div>
      {onClose && (
        <button className="btn-reset" onClick={onClose} style={{ color: 'var(--text-2)', padding: 2 }}>
          <Icon name="x" size={14} />
        </button>
      )}
    </div>
  );
}

// ── Skeleton blocks ─────────────────────────────────────────────────
function Skeleton({ w = '100%', h = 12, style }) {
  return <div className="skeleton" style={{ width: w, height: h, ...style }} />;
}

// ── Modal ───────────────────────────────────────────────────────────
function Modal({ title, body, danger, confirmLabel = 'Confirmar', cancelLabel = 'Cancelar', onConfirm, onCancel }) {
  return (
    <div onClick={onCancel} style={{
      position: 'absolute', inset: 0, background: 'rgba(0,0,0,.55)',
      display: 'flex', alignItems: 'center', justifyContent: 'center', zIndex: 100,
      backdropFilter: 'blur(2px)', animation: 'fade-in 150ms',
    }}>
      <div onClick={e => e.stopPropagation()} style={{
        width: 420, background: 'var(--bg-1)',
        border: '1px solid var(--border)', borderRadius: 'var(--r-lg)',
        padding: 20, boxShadow: '0 20px 60px rgba(0,0,0,.5)',
      }}>
        <div style={{ fontSize: 15, fontWeight: 600, color: 'var(--text-0)', marginBottom: 6 }}>{title}</div>
        <div style={{ fontSize: 13, color: 'var(--text-1)', marginBottom: 18 }}>{body}</div>
        <div style={{ display: 'flex', justifyContent: 'flex-end', gap: 8 }}>
          <Button variant="secondary" onClick={onCancel}>{cancelLabel}</Button>
          <Button variant={danger ? 'dangerSolid' : 'primary'} onClick={onConfirm}>{confirmLabel}</Button>
        </div>
      </div>
    </div>
  );
}

// ── Inputs ──────────────────────────────────────────────────────────
function TextInput({ value, onChange, placeholder, icon, style, autoFocus, kbd, size = 'md' }) {
  const heights = { sm: 28, md: 32, lg: 36 };
  const h = heights[size];
  return (
    <div style={{ position: 'relative', display: 'flex', alignItems: 'center', ...style }}>
      {icon && (
        <span style={{ position: 'absolute', left: 10, color: 'var(--text-2)', pointerEvents: 'none' }}>
          <Icon name={icon} size={14} />
        </span>
      )}
      <input
        type="text" value={value || ''} onChange={e => onChange && onChange(e.target.value)}
        placeholder={placeholder} autoFocus={autoFocus}
        style={{
          width: '100%', height: h,
          background: 'var(--bg-1)',
          border: '1px solid var(--border)',
          borderRadius: 'var(--r-md)',
          color: 'var(--text-0)',
          padding: `0 ${kbd ? 56 : 12}px 0 ${icon ? 32 : 12}px`,
          fontFamily: 'inherit', fontSize: 13,
          outline: 'none',
          transition: 'border-color var(--t-fast), background var(--t-fast)',
        }}
        onFocus={e => { e.target.style.borderColor = 'var(--accent)'; e.target.style.background = 'var(--bg-2)'; }}
        onBlur={e => { e.target.style.borderColor = 'var(--border)'; e.target.style.background = 'var(--bg-1)'; }}
      />
      {kbd && (
        <span className="mono" style={{
          position: 'absolute', right: 8,
          padding: '2px 6px', background: 'var(--bg-2)', border: '1px solid var(--border)',
          borderRadius: 4, color: 'var(--text-2)', fontSize: 10.5,
        }}>{kbd}</span>
      )}
    </div>
  );
}

// ── Section header ──────────────────────────────────────────────────
function SectionHead({ title, sub, right, level = 'h2' }) {
  const sizes = { h1: 26, h2: 18, h3: 14 };
  return (
    <div style={{ display: 'flex', alignItems: 'flex-end', justifyContent: 'space-between', marginBottom: 14, gap: 16 }}>
      <div>
        <div style={{ fontSize: sizes[level], fontWeight: 600, color: 'var(--text-0)', letterSpacing: -0.2 }}>{title}</div>
        {sub && <div style={{ fontSize: 12, color: 'var(--text-2)', marginTop: 2 }}>{sub}</div>}
      </div>
      {right}
    </div>
  );
}

// ── Helpers ─────────────────────────────────────────────────────────
function relTime(min) {
  if (min < 1) return 'ahora';
  if (min < 60) return `hace ${Math.round(min)} min`;
  const h = min / 60;
  if (h < 24) return `hace ${Math.round(h)} h`;
  const d = h / 24;
  if (d < 7) return `hace ${Math.round(d)} d`;
  return `hace ${Math.round(d / 7)} sem`;
}

// ── SupportButton ───────────────────────────────────────────────────
// Floating help button + popover with two contact paths. Reused on the
// login screen and inside the authenticated app's TopBar so users always
// have a way to reach support, even before they sign in.
const SUPPORT_WHATSAPP_DIRECT = 'https://wa.me/5218332096325';
const SUPPORT_WHATSAPP_GROUP  = 'https://chat.whatsapp.com/EZkpDTnpuRQ0FPIGpuT7SW?mode=gi_t';

function SupportButton({ variant = 'topbar' }) {
  const [open, setOpen] = React.useState(false);
  const btnRef = React.useRef(null);

  // Outside-click + Escape close the menu. Mirrors the Popover hardening
  // (don't fire when clicking inside the menu itself; mousedown vs click
  // sequencing matters).
  const menuRef = React.useRef(null);
  React.useEffect(() => {
    if (!open) return;
    const onDoc = (e) => {
      if (btnRef.current?.contains(e.target)) return;
      if (menuRef.current?.contains(e.target)) return;
      setOpen(false);
    };
    const onKey = (e) => { if (e.key === 'Escape') setOpen(false); };
    document.addEventListener('mousedown', onDoc);
    window.addEventListener('keydown', onKey);
    return () => { document.removeEventListener('mousedown', onDoc); window.removeEventListener('keydown', onKey); };
  }, [open]);

  // Two visual flavours: 'topbar' (racing-blue chat pill matching the
  // identity menu height) and 'corner' (floating bottom-right circle for
  // the auth screens). Both use a vibrant blue gradient + chat bubble
  // icon so the affordance reads as "talk to us" at a glance.
  const trigger = variant === 'corner' ? (
    <span style={{
      position: 'relative', display: 'inline-flex',
      width: 56, height: 56, borderRadius: '50%',
      animation: 'pulse-ring 2.4s ease-out infinite',
    }}>
      <button ref={btnRef} className="btn-reset" onClick={() => setOpen((o) => !o)}
        title="Ayuda y soporte"
        style={{
          width: 56, height: 56, borderRadius: '50%',
          display: 'flex', alignItems: 'center', justifyContent: 'center',
          background: 'linear-gradient(135deg, #3B82F6 0%, #2563EB 55%, #1D4ED8 100%)',
          border: '1px solid rgba(255,255,255,.18)',
          color: '#fff',
          boxShadow:
            '0 10px 28px -6px rgba(37,99,235,.65),' +
            '0 4px 12px -4px rgba(0,0,0,.45),' +
            'inset 0 1px 0 rgba(255,255,255,.25)',
          cursor: 'pointer',
          transition: 'transform 160ms cubic-bezier(.2,.7,.3,1)',
        }}
        onMouseEnter={(e) => e.currentTarget.style.transform = 'scale(1.06)'}
        onMouseLeave={(e) => e.currentTarget.style.transform = 'scale(1)'}>
        <Icon name="message-circle" size={22} />
      </button>
    </span>
  ) : (
    <button ref={btnRef} className="btn-reset" onClick={() => setOpen((o) => !o)}
      title="Ayuda y soporte"
      style={{
        height: 28, padding: '0 10px',
        display: 'flex', alignItems: 'center', gap: 6,
        borderRadius: 14,
        background: 'linear-gradient(135deg, #3B82F6 0%, #2563EB 100%)',
        color: '#fff', fontSize: 12, fontWeight: 600,
        boxShadow: '0 4px 10px -2px rgba(37,99,235,.45), inset 0 1px 0 rgba(255,255,255,.25)',
        border: '1px solid rgba(255,255,255,.15)',
      }}
      onMouseEnter={(e) => e.currentTarget.style.filter = 'brightness(1.1)'}
      onMouseLeave={(e) => e.currentTarget.style.filter = 'brightness(1)'}>
      <Icon name="message-circle" size={13} color="#fff" />
      <span>Soporte</span>
    </button>
  );

  // Anchored menu position: above the button for the corner variant, below
  // for the topbar variant. Computed at render time from the button rect.
  const menuStyle = (() => {
    if (!open || !btnRef.current) return null;
    const r = btnRef.current.getBoundingClientRect();
    if (variant === 'corner') {
      // Floating button is in viewport bottom-right; pop up + left.
      return { position: 'fixed', bottom: window.innerHeight - r.top + 8, right: window.innerWidth - r.right };
    }
    return { position: 'fixed', top: r.bottom + 6, right: window.innerWidth - r.right };
  })();

  const item = (href, icon, title, sub) => (
    <a href={href} target="_blank" rel="noreferrer"
      style={{
        display: 'flex', alignItems: 'flex-start', gap: 10, padding: '10px 12px',
        borderRadius: 6, color: 'var(--text-0)', textDecoration: 'none',
        cursor: 'pointer',
      }}
      onMouseEnter={(e) => e.currentTarget.style.background = 'var(--bg-2)'}
      onMouseLeave={(e) => e.currentTarget.style.background = 'transparent'}
      onClick={() => setOpen(false)}>
      <span style={{
        width: 28, height: 28, borderRadius: 6,
        background: 'rgba(37,211,102,.12)', color: '#25D366',
        display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0,
      }}>
        <Icon name={icon} size={14} />
      </span>
      <div style={{ minWidth: 0 }}>
        <div style={{ fontSize: 12.5, fontWeight: 600, color: 'var(--text-0)' }}>{title}</div>
        <div style={{ fontSize: 11, color: 'var(--text-2)', marginTop: 1 }}>{sub}</div>
      </div>
    </a>
  );

  return (
    <>
      {trigger}
      {open && menuStyle && (
        <div ref={menuRef} style={{
          ...menuStyle, zIndex: 200, width: 280, padding: 4,
          background: 'var(--bg-1)', border: '1px solid var(--border-strong)',
          borderRadius: 'var(--r-md)', boxShadow: '0 16px 40px rgba(0,0,0,.55)',
        }}>
          <div style={{ padding: '10px 12px 6px', fontSize: 10.5, color: 'var(--text-3)', letterSpacing: 0.6, textTransform: 'uppercase', fontWeight: 600 }}>
            Soporte
          </div>
          {item(SUPPORT_WHATSAPP_DIRECT, 'message-circle', 'WhatsApp directo', 'Habla con Sebastián 1 a 1')}
          {item(SUPPORT_WHATSAPP_GROUP, 'users', 'Grupo de soporte', 'Comunidad de usuarios + equipo')}
        </div>
      )}
    </>
  );
}

Object.assign(window, {
  ACTORS, ACTOR_LIST, Icon, Avatar, AvatarStack,
  Button, Badge, PriorityChip, STATUS_META, PRIORITY_META, StatusDot, Tag,
  Toast, Skeleton, Modal, TextInput, SectionHead, relTime,
  SupportButton,
});
