// Shared primitives — My Geni design system foundation.
// Palette consumed from tokens.css. Layout/spacing from styles.css.

const AVATAR_COLORS = [
  '#2A85FF', '#00A656', '#FF9D34', '#7F5FFF',
  '#F52495', '#FF381C', '#0EA5A5', '#B37500',
  '#6E7CFF', '#7A6B54',
];

function initialsOf(name) {
  const parts = name.trim().split(/\s+/);
  if (parts.length === 1) return parts[0].slice(0, 2).toUpperCase();
  return (parts[0][0] + parts[parts.length - 1][0]).toUpperCase();
}

function colorFromSeed(seed) {
  let h = 0;
  for (let i = 0; i < seed.length; i++) h = (h * 31 + seed.charCodeAt(i)) >>> 0;
  return AVATAR_COLORS[h % AVATAR_COLORS.length];
}

// Look up a portrait for a named person if project data has one.
function _portraitFor(name) {
  if (!name) return null;
  const pdata = window.MYGENI_PROJECT_DATA && window.MYGENI_PROJECT_DATA['p-mh-portal'];
  const map = pdata && pdata.people;
  if (!map) return null;
  const p = map[String(name).toLowerCase()];
  return p ? p.portrait : null;
}

function Avatar({ name, size = 'md', bg, portrait }) {
  const src = portrait || _portraitFor(name);
  const initials = initialsOf(name);
  const color = bg || colorFromSeed(name);
  if (src) {
    return (
      <div className={`avatar ${size} avatar-photo`} title={name}>
        <img src={src} alt={name} loading="lazy" />
      </div>
    );
  }
  return (
    <div className={`avatar ${size}`} style={{ background: color }} title={name}>
      {initials}
    </div>
  );
}

function AvatarStack({ names, max = 3, size = 'md' }) {
  const shown = names.slice(0, max);
  const extra = names.length - shown.length;
  return (
    <div className="avatar-stack">
      {shown.map((n, i) => <Avatar key={i} name={n} size={size} />)}
      {extra > 0 && (<div className={`avatar ${size} more`}>+{extra}</div>)}
    </div>
  );
}

// Core 2.0-style label: subtle single-color pill with 1.5px border
function Label({ tone = 'gray', children, dot = false, size, style, icon }) {
  return (
    <span className={`label label-${tone}${size === 'sm' ? ' label-sm' : ''}`} style={style}>
      {dot && <span className="cdot" />}
      {icon}
      {children}
    </span>
  );
}

function ProgressBar({ value = 0, tone }) {
  return (
    <div className={`pbar ${tone || ''}`}>
      <i style={{ width: `${Math.max(0, Math.min(100, value))}%` }} />
    </div>
  );
}

function Card({ children, style, className = '' }) {
  return <div className={`card ${className}`} style={style}>{children}</div>;
}

function CardHead({ title, count, extra, right, icon }) {
  return (
    <div className="card-head">
      <h3>
        {icon && icon}
        {title}
        {count != null && <span className="count">· {count}</span>}
      </h3>
      {right || (extra && <a className="link">{extra}</a>)}
    </div>
  );
}

// Progress ring — Core 2.0 style with white plug
function Ring({ percent = 40, color = 'var(--warning-solid)', size = 56, label }) {
  return (
    <div className="ring" style={{ '--sz': size + 'px', '--pct': percent, '--fill': color }}>
      <span>{label != null ? label : `${percent}%`}</span>
    </div>
  );
}

// Sparkline — a thin trend line
function Sparkline({ points, color = 'var(--primary-01)', width = 84, height = 24, fill = true }) {
  if (!points || points.length < 2) return null;
  const min = Math.min(...points);
  const max = Math.max(...points);
  const span = max - min || 1;
  const step = width / (points.length - 1);
  const coords = points.map((v, i) => [i * step, height - ((v - min) / span) * (height - 3) - 1.5]);
  const linePath = coords.map(([x, y], i) => `${i === 0 ? 'M' : 'L'}${x.toFixed(1)} ${y.toFixed(1)}`).join(' ');
  const areaPath = `${linePath} L${width} ${height} L0 ${height} Z`;
  const fillId = 'sp-' + Math.random().toString(36).slice(2, 8);
  return (
    <svg width={width} height={height} className="spark" aria-hidden="true">
      {fill && (
        <>
          <defs>
            <linearGradient id={fillId} x1="0" y1="0" x2="0" y2="1">
              <stop offset="0%" stopColor={color} stopOpacity="0.22" />
              <stop offset="100%" stopColor={color} stopOpacity="0" />
            </linearGradient>
          </defs>
          <path d={areaPath} fill={`url(#${fillId})`} />
        </>
      )}
      <path d={linePath} fill="none" stroke={color} strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
    </svg>
  );
}

// Mini bars — activity bursts
function MiniBars({ points, color = 'var(--primary-01)', width = 84, height = 24 }) {
  const max = Math.max(...points) || 1;
  const barW = Math.max(2, (width - (points.length - 1) * 2) / points.length);
  return (
    <svg width={width} height={height} aria-hidden="true">
      {points.map((v, i) => {
        const h = Math.max(2, (v / max) * (height - 2));
        return <rect key={i} x={i * (barW + 2)} y={height - h} width={barW} height={h} rx="1.5" fill={color} opacity={0.35 + (v/max)*0.55} />;
      })}
    </svg>
  );
}

// Tabs (segmented)
function Tabs({ items, value, onChange }) {
  return (
    <div className="tabs" role="tablist">
      {items.map((it) => {
        const key = typeof it === 'string' ? it : it.key;
        const label = typeof it === 'string' ? it : it.label;
        const count = typeof it === 'string' ? null : it.count;
        const tone  = typeof it === 'string' ? null : it.tone;
        const active = value === key;
        return (
          <button
            key={key}
            role="tab"
            aria-selected={active}
            className={`tab${active ? ' active' : ''}`}
            onClick={() => onChange && onChange(key)}
          >
            <span>{label}</span>
            {count != null && (
              <span className={`tab-count${tone ? ' tone-'+tone : ''}`}>{count}</span>
            )}
          </button>
        );
      })}
    </div>
  );
}

// Text input
function Input({ leadIcon, trailIcon, placeholder, value, onChange, style, className = '' }) {
  return (
    <label className={`input ${className}`} style={style}>
      {leadIcon && <span className="input-lead">{leadIcon}</span>}
      <input
        placeholder={placeholder}
        value={value}
        onChange={onChange ? (e) => onChange(e.target.value) : undefined}
      />
      {trailIcon && <span className="input-trail">{trailIcon}</span>}
    </label>
  );
}

// Select (looks like a select — actual popup is out of scope for a system view)
function Select({ label, value, style }) {
  return (
    <button className="select" style={style}>
      {label && <span className="select-label">{label}</span>}
      <span className="select-value">{value}</span>
      <Icon name="caretDown" size={14} strokeWidth={1.75} />
    </button>
  );
}

// Empty state
function EmptyState({ illustration, title, body, action }) {
  return (
    <div className="empty">
      {illustration && <div className="empty-illus">{illustration}</div>}
      <div className="empty-title">{title}</div>
      {body && <div className="empty-body">{body}</div>}
      {action && <div className="empty-action">{action}</div>}
    </div>
  );
}

// Doc thumbnail (used in file lists & contract previews)
function DocThumb({ kind = 'doc', title, meta, size = 'md' }) {
  return (
    <div className={`doc-thumb doc-${kind} doc-${size}`}>
      <div className="doc-thumb-visual">
        <DocVisual kind={kind} />
        <span className="doc-badge">{kind.toUpperCase()}</span>
      </div>
      {title && (
        <div className="doc-thumb-meta">
          <div className="doc-thumb-title">{title}</div>
          {meta && <div className="doc-thumb-sub">{meta}</div>}
        </div>
      )}
    </div>
  );
}

function DocVisual({ kind }) {
  if (kind === 'contract') {
    return (
      <svg viewBox="0 0 240 138" width="100%" height="100%" preserveAspectRatio="none">
        <rect x="46" y="16" width="148" height="106" rx="6" fill="#FDFDFD" stroke="rgba(16,16,16,0.10)" strokeWidth="1.5" />
        <path d="M172 16 L172 32 L194 32" fill="#F1F1F1" stroke="rgba(16,16,16,0.10)" strokeWidth="1.5" />
        <g stroke="rgba(16,16,16,0.22)" strokeWidth="1.5" strokeLinecap="round">
          <path d="M58 40 h60" />
          <path d="M58 52 h100" />
          <path d="M58 62 h80" />
          <path d="M58 72 h90" />
          <path d="M58 82 h70" />
          <path d="M58 92 h84" />
        </g>
        <path d="M144 104 c8 -8 16 -2 22 -8" stroke="#2A85FF" strokeWidth="1.75" fill="none" strokeLinecap="round" />
      </svg>
    );
  }
  if (kind === 'diagram') {
    return (
      <svg viewBox="0 0 240 138" width="100%" height="100%" preserveAspectRatio="none">
        <rect x="18" y="30" width="66" height="30" rx="8" fill="#FDFDFD" stroke="rgba(16,16,16,0.10)" strokeWidth="1.5" />
        <rect x="102" y="12" width="66" height="30" rx="8" fill="#FDFDFD" stroke="rgba(16,16,16,0.10)" strokeWidth="1.5" />
        <rect x="102" y="54" width="66" height="30" rx="8" fill="#2A85FF" stroke="rgba(42,133,255,0.5)" strokeWidth="1.5" />
        <rect x="102" y="96" width="66" height="30" rx="8" fill="#FDFDFD" stroke="rgba(16,16,16,0.10)" strokeWidth="1.5" />
        <rect x="186" y="54" width="60" height="30" rx="8" fill="#FDFDFD" stroke="rgba(16,16,16,0.10)" strokeWidth="1.5" />
        <path d="M84 45 L102 27" stroke="rgba(16,16,16,0.25)" strokeWidth="1.5" fill="none" strokeLinecap="round"/>
        <path d="M84 45 L102 69" stroke="#2A85FF" strokeWidth="1.75" fill="none" strokeLinecap="round"/>
        <path d="M84 45 L102 111" stroke="rgba(16,16,16,0.25)" strokeWidth="1.5" fill="none" strokeLinecap="round"/>
        <path d="M168 69 L186 69" stroke="#2A85FF" strokeWidth="1.75" fill="none" strokeLinecap="round"/>
      </svg>
    );
  }
  if (kind === 'chart') {
    return (
      <svg viewBox="0 0 240 138" width="100%" height="100%" preserveAspectRatio="none">
        {[35,68,101].map(y => (<line key={y} x1="14" y1={y} x2="226" y2={y} stroke="rgba(16,16,16,0.05)" strokeWidth="1"/>))}
        {[42,58,50,74,66,92,80,102,88,116,104,124].map((h, i) => (
          <rect key={i} x={18 + i*19} y={126 - h} width="11" height={h} rx="3" fill="rgba(42,133,255,0.18)" />
        ))}
        <path d="M23 90 L42 76 L61 82 L80 58 L99 66 L118 40 L137 50 L156 26 L175 34 L194 14 L213 24 L232 12" stroke="#00A656" strokeWidth="2" fill="none" strokeLinecap="round" strokeLinejoin="round"/>
        {[[23,90],[42,76],[80,58],[137,50],[194,14],[232,12]].map(([x,y], i) => (<circle key={i} cx={x} cy={y} r="2.5" fill="white" stroke="#00A656" strokeWidth="1.5" />))}
      </svg>
    );
  }
  if (kind === 'frames') {
    return (
      <div style={{ padding: 14, display: 'flex', gap: 8, height: '100%' }}>
        {[0,1,2].map(i => (
          <div key={i} style={{
            flex: 1, background: 'white',
            border: '1.5px solid rgba(16,16,16,0.08)',
            borderRadius: 10, padding: 8, display: 'flex', flexDirection: 'column', gap: 4,
            boxShadow: '0 2px 4px rgba(16,16,16,0.04)',
          }}>
            <div style={{ height: 5, width: '65%', background: 'rgba(16,16,16,0.24)', borderRadius: 2 }} />
            <div style={{ height: 3, width: '92%', background: 'rgba(16,16,16,0.10)', borderRadius: 2 }} />
            <div style={{ height: 3, width: '72%', background: 'rgba(16,16,16,0.10)', borderRadius: 2 }} />
            <div style={{
              flex: 1,
              background: i === 1 ? 'linear-gradient(180deg,#E9F1FF,#D6E4FF)' : 'rgba(16,16,16,0.04)',
              borderRadius: 4, marginTop: 4,
              display: 'flex', alignItems: 'flex-end', padding: 3,
            }}>
              {i === 1 && <div style={{ height: 6, width: 24, background: '#2A85FF', borderRadius: 2 }} />}
            </div>
          </div>
        ))}
      </div>
    );
  }
  // default: doc (text lines)
  return (
    <svg viewBox="0 0 240 138" width="100%" height="100%" preserveAspectRatio="none">
      <g transform="translate(20 16)">
        <rect width="60%" height="8" y="0" fill="rgba(16,16,16,0.28)" rx="3" />
        <rect width="46%" height="5" y="14" fill="rgba(16,16,16,0.14)" rx="2" />
        <rect width="88%" height="5" y="28" fill="rgba(16,16,16,0.10)" rx="2" />
        <rect width="78%" height="5" y="38" fill="rgba(16,16,16,0.10)" rx="2" />
        <rect width="82%" height="5" y="48" fill="rgba(16,16,16,0.10)" rx="2" />
        <rect width="70%" height="5" y="58" fill="rgba(16,16,16,0.10)" rx="2" />
        <rect width="90%" height="5" y="68" fill="rgba(16,16,16,0.10)" rx="2" />
        <rect width="60%" height="5" y="78" fill="rgba(16,16,16,0.10)" rx="2" />
        <rect width="72%" height="5" y="94" fill="rgba(16,16,16,0.10)" rx="2" />
      </g>
    </svg>
  );
}

// Contract chip — small "paper-fold" pill that identifies a contract on a project
function ContractChip({ id, tone = 'gray', style }) {
  return (
    <span className={`contract-chip contract-${tone}`} style={style} title={`Contract ${id}`}>
      <svg width="12" height="12" viewBox="0 0 24 24" fill="none" aria-hidden="true">
        <path d="M14 3H6a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V9l-6-6Zm0 0v6h6" stroke="currentColor" strokeWidth="1.75" strokeLinecap="round" strokeLinejoin="round"/>
      </svg>
      <span className="mono">{id}</span>
    </span>
  );
}

// Client chip — small avatar-tinted pill
function ClientChip({ client, style }) {
  if (!client) return null;
  return (
    <span className="client-chip" style={style} title={client.name}>
      <span className="client-chip-mark" style={{ background: client.color }}>{client.short}</span>
      <span className="truncate">{client.name}</span>
    </span>
  );
}

// -----------------------------------------------------------------
// RelatedTo — a compact context chip showing the stage/mission/feature
// a canonical item is linked to. Restrained: one line, tertiary text,
// icon-first. Used across Research, Design, Dev, Docs, Contracts.
// -----------------------------------------------------------------
function RelatedTo({ links, pdata, compact, asSpan }) {
  if (!links || !pdata) return null;
  const stage   = links.stage   && (pdata.stages || []).find(s => s.n === links.stage);
  const missionMeta = links.mission && (window.MYGENI_MISSIONS || {})[links.mission];
  const feature = links.feature && (pdata.features || []).find(f => f.id === links.feature);
  const parts = [];

  if (stage) {
    parts.push({
      icon: 'stages',
      label: `Stage ${String(stage.n).padStart(2, '0')} · ${stage.name}`,
      href: `#/projects/${pdata.id}/plan`,
    });
  }
  if (missionMeta) {
    parts.push({
      icon: missionMeta.icon,
      label: missionMeta.label,
      href: `#/projects/${pdata.id}/plan`,
    });
  }
  if (feature) {
    parts.push({
      icon: 'features',
      label: feature.title,
      href: `#/projects/${pdata.id}/features/${feature.id}`,
    });
  }
  if (parts.length === 0) return null;

  // When this chip is rendered INSIDE another anchor (e.g. a card that wraps
  // the entire row in an <a>), nested <a> is invalid HTML. Passing asSpan
  // renders the tokens as <span>s that still visually match the chip but
  // don't nest. Click bubbles up to the parent link naturally.
  const Tok = asSpan ? 'span' : 'a';
  const tokProps = asSpan ? {} : { onClick: (e) => e.stopPropagation() };

  return (
    <div className={`related-to${compact ? ' related-to-compact' : ''}`}>
      <span className="rt-lbl">Related to</span>
      {parts.map((p, i) => (
        <React.Fragment key={i}>
          {i > 0 && <span className="rt-sep">·</span>}
          <Tok
            {...(asSpan ? {} : { href: p.href })}
            className="rt-chip"
            {...tokProps}
          >
            <Icon name={p.icon} size={10} strokeWidth={1.75} />
            <span>{p.label}</span>
          </Tok>
        </React.Fragment>
      ))}
    </div>
  );
}

Object.assign(window, {
  Avatar, AvatarStack, Label, ProgressBar, Card, CardHead, Ring,
  Sparkline, MiniBars, Tabs, Input, Select, EmptyState,
  DocThumb, DocVisual, ContractChip, ClientChip,
  RelatedTo,
  initialsOf, colorFromSeed,
});
