// Shared primitives — paper textures, stamps, icons, ticket effects
(function(){
const COLORS = {
  paper: '#FFFFFF',         // white background
  paperDeep: '#E8F8F4',     // light mint — card surfaces (tint of #76d0c0)
  paperEdge: '#A8DDD2',     // softer mint — borders / hairlines
  ink: '#1A2566',           // dark navy — primary text (derived from #4a62d8)
  inkSoft: '#746BAB',       // purple — secondary text
  teal: '#4A62D8',          // primary brand / action — indigo blue
  tealDeep: '#3550B8',      // hover / active — darker indigo
  clay: '#746BAB',          // long waits / urgency — purple (no red in palette)
  mustard: '#FBBE4F',       // highlights / achievements — golden yellow
  moss: '#1AB28A',          // short waits / success — green
  ribbon: '#746BAB',         // premium / rare — purple
  // Extra accents kept for tweak options + charts
  cobalt: '#4A62D8',
  grape:  '#746BAB',
  mint:   '#76D0C0',         // light teal — accent / chart fills
};

// Wait-time semantic color (short = green, medium = yellow, long = purple)
const waitColor = (m) => {
  if (m < 20) return COLORS.moss;
  if (m <= 45) return COLORS.mustard;
  return COLORS.clay;
};

// Paper noise overlay — SVG turbulence (subtle so vivid colors stay vivid)
const PAPER_NOISE = `data:image/svg+xml;utf8,${encodeURIComponent(
  `<svg xmlns="http://www.w3.org/2000/svg" width="220" height="220">
    <filter id="n">
      <feTurbulence type="fractalNoise" baseFrequency="0.9" numOctaves="2" stitchTiles="stitch"/>
      <feColorMatrix values="0 0 0 0 0.18  0 0 0 0 0.14  0 0 0 0 0.06  0 0 0 0.35 0"/>
    </filter>
    <rect width="100%" height="100%" filter="url(#n)" opacity="0.32"/>
  </svg>`
)}`;

const PaperBg = ({ style = {}, children }) => (
  <div style={{
    background: COLORS.paper,
    backgroundImage: `url("${PAPER_NOISE}")`,
    backgroundSize: '220px 220px',
    ...style,
  }}>{children}</div>
);

// Stamp number — JetBrains Mono with unit at 0.5em
const Stamp = ({ value, unit, size = 32, color = COLORS.ink, weight = 700 }) => (
  <span style={{
    fontFamily: '"JetBrains Mono", monospace',
    fontWeight: weight,
    fontSize: size,
    color,
    letterSpacing: '-0.02em',
    lineHeight: 1,
    display: 'inline-flex',
    alignItems: 'baseline',
    fontVariantNumeric: 'tabular-nums',
  }}>
    {value}
    {unit && (
      <span style={{
        fontSize: size * 0.42,
        opacity: 0.6,
        marginLeft: size * 0.12,
        fontWeight: 600,
        letterSpacing: '0.04em',
      }}>{unit}</span>
    )}
  </span>
);

// Ink-stamped wait pill — slightly imperfect look
// StampPill renders the wait-time pill. Accepts either:
//   <StampPill value={42} />                      → 42 min, color by waitColor
//   <StampPill attraction={a} />                  → smart: handles closed / loading / live / shows
// Attraction types that don't have a queue wait — they get a category label
// instead of a misleading "0 min" pill.
const NON_WAIT_TYPES = new Set(['Show', 'Walkthrough', 'Character', 'Play Area', 'Interactive']);
const StampPill = ({ value, unit = 'min', size = 28, attraction }) => {
  // When given an attraction, derive the display state from its full shape
  if (attraction) {
    if (attraction.type === 'Show') {
      return <PillStub label="Show" color={COLORS.mustard} size={size} />;
    }
    if (NON_WAIT_TYPES.has(attraction.type)) {
      const label = attraction.type === 'Walkthrough' ? 'Walk-thru' : attraction.type;
      return <PillStub label={label} color={COLORS.inkSoft} size={size} />;
    }
    if (attraction.isOpen === false) {
      return <PillStub label="Closed" color={COLORS.inkSoft} size={size} />;
    }
    if (!attraction.lastUpdated) {
      return <PillStub label="…" color={COLORS.inkSoft} size={size} title="Loading live wait" />;
    }
    value = attraction.wait;
  }
  const safe = typeof value === 'number' ? value : 0;
  const color = waitColor(safe);
  return (
    <div style={{
      display: 'inline-flex',
      alignItems: 'baseline',
      padding: '4px 10px 5px',
      borderRadius: 6,
      border: `1.5px solid ${color}`,
      background: 'transparent',
      transform: `rotate(${(safe % 5 - 2) * 0.3}deg)`,
      filter: 'contrast(1.05)',
    }}>
      <Stamp value={safe} unit={unit} size={size} color={color} />
    </div>
  );
};

const PillStub = ({ label, color, size = 28, title }) => (
  <div title={title} style={{
    display: 'inline-flex', alignItems: 'center',
    padding: '4px 10px 5px', borderRadius: 6,
    border: `1.5px dashed ${color}`,
    fontFamily: '"JetBrains Mono", monospace',
    fontWeight: 600, fontSize: Math.max(11, size * 0.5), color,
  }}>{label}</div>
);

// Card — paper-deep with edge border
const Card = ({ children, style = {}, perforated = false, onClick }) => {
  const base = {
    background: COLORS.paperDeep,
    border: `1px solid ${COLORS.paperEdge}`,
    borderRadius: 12,
    padding: '16px',
    boxShadow: '0 4px 12px rgba(26,37,102,0.06)',
    cursor: onClick ? 'pointer' : 'default',
    position: 'relative',
    ...style,
  };
  if (perforated) {
    base.maskImage = 'radial-gradient(circle at 0 50%, transparent 4px, black 4.5px), radial-gradient(circle at 100% 50%, transparent 4px, black 4.5px)';
    base.WebkitMaskImage = base.maskImage;
    base.maskComposite = 'intersect';
    base.WebkitMaskComposite = 'source-in';
  }
  return <div onClick={onClick} style={base}>{children}</div>;
};

// Caption — uppercase tracked
const Caption = ({ children, color = COLORS.inkSoft, style = {} }) => (
  <div style={{
    fontFamily: 'Manrope, system-ui, sans-serif',
    fontSize: 11,
    fontWeight: 600,
    textTransform: 'uppercase',
    letterSpacing: '0.14em',
    color,
    ...style,
  }}>{children}</div>
);

// Display title (Fraunces)
const Display = ({ children, size = 28, weight = 600, italic = false, style = {} }) => (
  <div style={{
    fontFamily: 'Fraunces, Georgia, serif',
    fontWeight: weight,
    fontSize: size,
    lineHeight: 1.05,
    letterSpacing: '-0.01em',
    fontStyle: italic ? 'italic' : 'normal',
    color: COLORS.ink,
    fontVariationSettings: '"opsz" 144',
    ...style,
  }}>{children}</div>
);

// Body text (Manrope)
const Body = ({ children, size = 15, weight = 400, color = COLORS.ink, style = {} }) => (
  <div style={{
    fontFamily: 'Manrope, system-ui, sans-serif',
    fontWeight: weight,
    fontSize: size,
    lineHeight: 1.45,
    color,
    ...style,
  }}>{children}</div>
);

// Perforated divider — circles cut along a horizontal line, with caption text
const PerforatedDivider = ({ label, style = {} }) => (
  <div style={{
    display: 'flex',
    alignItems: 'center',
    gap: 10,
    margin: '4px 0',
    ...style,
  }}>
    <PerfLine />
    {label && (
      <div style={{
        fontFamily: 'Manrope, system-ui, sans-serif',
        fontSize: 10,
        fontWeight: 700,
        textTransform: 'uppercase',
        letterSpacing: '0.18em',
        color: COLORS.inkSoft,
        whiteSpace: 'nowrap',
      }}>{label}</div>
    )}
    <PerfLine />
  </div>
);
const PerfLine = () => (
  <div style={{
    flex: 1,
    height: 8,
    backgroundImage: `radial-gradient(circle at center, ${COLORS.paperEdge} 1.6px, transparent 2px)`,
    backgroundSize: '8px 8px',
    backgroundPosition: 'center',
    backgroundRepeat: 'repeat-x',
  }} />
);

// "Stamped" overlay text — for completed checklist items
const StampedOverlay = ({ text, color = COLORS.clay, rotate = -4 }) => (
  <div style={{
    fontFamily: '"JetBrains Mono", monospace',
    fontWeight: 700,
    fontSize: 14,
    letterSpacing: '0.18em',
    color,
    border: `2px solid ${color}`,
    padding: '3px 8px',
    borderRadius: 4,
    transform: `rotate(${rotate}deg)`,
    opacity: 0.85,
    background: 'transparent',
    display: 'inline-block',
    filter: 'contrast(1.1)',
    textShadow: '0 0 1px currentColor',
  }}>{text}</div>
);

// Ticket-stub rating icon (5 stubs)
const TicketStub = ({ filled = true, size = 18, color = COLORS.mustard }) => (
  <svg width={size} height={size * 0.7} viewBox="0 0 20 14" style={{ marginRight: 2 }}>
    <path
      d="M2 1 H18 V13 H2 Z M5 1 V13 M15 1 V13"
      fill={filled ? color : 'none'}
      stroke={filled ? color : COLORS.paperEdge}
      strokeWidth="1.5"
      strokeDasharray={filled ? 'none' : '2 2'}
    />
  </svg>
);

// Icon set (linework) for tab bar and small UI
const Icon = ({ name, size = 22, color = COLORS.ink, strokeWidth = 1.6 }) => {
  const sw = strokeWidth;
  const props = { width: size, height: size, viewBox: '0 0 24 24', fill: 'none', stroke: color, strokeWidth: sw, strokeLinecap: 'round', strokeLinejoin: 'round' };
  const paths = {
    home: <><path d="M3 11 L12 4 L21 11 V20 A1 1 0 0 1 20 21 H4 A1 1 0 0 1 3 20 Z"/><path d="M9 21 V14 H15 V21"/></>,
    waits: <><circle cx="12" cy="12" r="8"/><path d="M12 7 V12 L15 14"/></>,
    map: <><path d="M3 6 L9 4 L15 6 L21 4 V18 L15 20 L9 18 L3 20 Z"/><path d="M9 4 V18 M15 6 V20"/></>,
    list: <><path d="M4 7 H20 M4 12 H20 M4 17 H20"/><circle cx="6" cy="7" r="0.6" fill={color}/><circle cx="6" cy="12" r="0.6" fill={color}/><circle cx="6" cy="17" r="0.6" fill={color}/></>,
    user: <><circle cx="12" cy="8" r="4"/><path d="M4 21 C4 16 8 14 12 14 C16 14 20 16 20 21"/></>,
    chevron: <path d="M9 6 L15 12 L9 18"/>,
    chevronDown: <path d="M6 9 L12 15 L18 9"/>,
    chevronUp: <path d="M6 15 L12 9 L18 15"/>,
    pin: <><path d="M12 21 C8 17 5 13 5 9 A7 7 0 0 1 19 9 C19 13 16 17 12 21 Z"/><circle cx="12" cy="9" r="2.5"/></>,
    star: <path d="M12 3 L14.5 9 L21 9.5 L16 13.5 L17.5 20 L12 16.5 L6.5 20 L8 13.5 L3 9.5 L9.5 9 Z"/>,
    starFilled: <path d="M12 3 L14.5 9 L21 9.5 L16 13.5 L17.5 20 L12 16.5 L6.5 20 L8 13.5 L3 9.5 L9.5 9 Z" fill={color}/>,
    target: <><circle cx="12" cy="12" r="9"/><circle cx="12" cy="12" r="5"/><circle cx="12" cy="12" r="1.5" fill={color}/></>,
    refresh: <><path d="M21 12 A9 9 0 1 1 12 3 V8"/><path d="M21 3 V8 H16"/></>,
    filter: <path d="M4 5 H20 L14 12 V19 L10 21 V12 Z"/>,
    plus: <><path d="M12 5 V19 M5 12 H19"/></>,
    check: <path d="M5 12 L10 17 L19 7"/>,
    close: <path d="M6 6 L18 18 M18 6 L6 18"/>,
    sparkle: <><path d="M12 3 L13 10 L20 11 L13 12 L12 19 L11 12 L4 11 L11 10 Z"/></>,
    flag: <><path d="M5 21 V4 H17 L15 8 L17 12 H5"/></>,
    sun: <><circle cx="12" cy="12" r="4"/><path d="M12 2 V4 M12 20 V22 M2 12 H4 M20 12 H22 M5 5 L6.5 6.5 M17.5 17.5 L19 19 M5 19 L6.5 17.5 M17.5 6.5 L19 5"/></>,
    moon: <path d="M20 14 A8 8 0 1 1 10 4 A6 6 0 0 0 20 14 Z"/>,
    train: <><rect x="5" y="4" width="14" height="14" rx="2"/><path d="M5 14 H19 M9 4 V14 M15 4 V14"/><circle cx="9" cy="20" r="1"/><circle cx="15" cy="20" r="1"/></>,
    settings: <><circle cx="12" cy="12" r="3"/><path d="M12 2 V5 M12 19 V22 M22 12 H19 M5 12 H2 M19 5 L17 7 M7 17 L5 19 M19 19 L17 17 M7 7 L5 5"/></>,
    calendar: <><rect x="3" y="5" width="18" height="16" rx="1"/><path d="M3 10 H21 M8 3 V7 M16 3 V7"/></>,
    car: <><path d="M5 17 H3 V13 L5 8 H19 L21 13 V17 H19"/><path d="M5 13 H19"/><circle cx="7.5" cy="17" r="1.6"/><circle cx="16.5" cy="17" r="1.6"/></>,
    navigate: <><path d="M3 11 L21 4 L14 21 L12 13 Z"/></>,
    trash: <><path d="M5 7 H19 M9 7 V5 A1 1 0 0 1 10 4 H14 A1 1 0 0 1 15 5 V7 M7 7 L8 20 A1 1 0 0 0 9 21 H15 A1 1 0 0 0 16 20 L17 7"/></>,
    ears: <><path d="M4 15 V12 A8 8 0 0 1 20 12 V15"/><rect x="3" y="14" width="4" height="6" rx="1.8"/><rect x="17" y="14" width="4" height="6" rx="1.8"/></>,
    quiz: <><circle cx="12" cy="12" r="9"/><path d="M9 9 A3 3 0 0 1 15 9 C15 11 12 11.5 12 14"/><circle cx="12" cy="17.5" r="0.8" fill={color}/></>,
  };
  return <svg {...props}>{paths[name]}</svg>;
};

// Sparkline (mini SVG line chart)
const Sparkline = ({ points, width = 120, height = 36, color = COLORS.teal, strokeWidth = 1.5 }) => {
  if (!points || !points.length) return null;
  const xs = points.map(p => p.h);
  const ys = points.map(p => p.v);
  const xmin = Math.min(...xs), xmax = Math.max(...xs);
  const ymin = 0, ymax = Math.max(...ys, 60);
  const px = (x) => ((x - xmin) / (xmax - xmin)) * width;
  const py = (y) => height - ((y - ymin) / (ymax - ymin)) * height;
  const d = points.map((p, i) => `${i === 0 ? 'M' : 'L'} ${px(p.h).toFixed(1)} ${py(p.v).toFixed(1)}`).join(' ');
  return (
    <svg width={width} height={height} style={{ display: 'block' }}>
      <path d={d} fill="none" stroke={color} strokeWidth={strokeWidth} strokeLinecap="round" strokeLinejoin="round" />
    </svg>
  );
};

// Progress bar with bullets — for favorite-line and achievement progress
const BulletProgress = ({ value, total = 8, color = COLORS.teal, dotSize = 7 }) => {
  const filled = Math.round(value * total);
  return (
    <div style={{ display: 'flex', gap: 4, alignItems: 'center' }}>
      {Array.from({ length: total }).map((_, i) => (
        <div key={i} style={{
          width: dotSize,
          height: dotSize,
          borderRadius: '50%',
          background: i < filled ? color : 'transparent',
          border: `1.2px solid ${i < filled ? color : COLORS.paperEdge}`,
        }} />
      ))}
    </div>
  );
};

// Bar progress (for achievements)
const BarProgress = ({ value, total, color = COLORS.mustard, height = 8, showLabel = false }) => {
  const pct = Math.min(100, (value / total) * 100);
  return (
    <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
      <div style={{
        flex: 1,
        height,
        background: 'rgba(26,37,102,0.08)',
        borderRadius: height,
        overflow: 'hidden',
        border: `0.5px solid ${COLORS.paperEdge}`,
      }}>
        <div style={{
          width: `${pct}%`,
          height: '100%',
          background: color,
        }} />
      </div>
      {showLabel && (
        <span style={{
          fontFamily: '"JetBrains Mono", monospace',
          fontSize: 11,
          fontWeight: 700,
          color: COLORS.inkSoft,
        }}>{value}/{total}</span>
      )}
    </div>
  );
};

// Achievement-style stamp icon (a circular wax-stamp look)
const StampIcon = ({ tier = 'mustard', size = 56, locked = false, glyph = '★' }) => {
  const colorMap = {
    mustard: COLORS.mustard,
    moss: COLORS.moss,
    teal: COLORS.teal,
    clay: COLORS.clay,
    ribbon: COLORS.ribbon,
  };
  const c = locked ? COLORS.paperEdge : colorMap[tier] || COLORS.mustard;
  return (
    <div style={{
      width: size,
      height: size,
      borderRadius: '50%',
      border: `2px solid ${c}`,
      display: 'flex',
      alignItems: 'center',
      justifyContent: 'center',
      position: 'relative',
      opacity: locked ? 0.35 : 1,
    }}>
      <div style={{
        position: 'absolute',
        inset: 4,
        borderRadius: '50%',
        border: `1px dashed ${c}`,
      }} />
      <span style={{
        fontFamily: 'Fraunces, serif',
        fontWeight: 700,
        fontSize: size * 0.4,
        color: c,
      }}>{glyph}</span>
    </div>
  );
};

window.PH365_UI = {
  COLORS, waitColor, PAPER_NOISE,
  PaperBg, Stamp, StampPill, PillStub, Card, Caption, Display, Body,
  PerforatedDivider, StampedOverlay, TicketStub, Icon,
  Sparkline, BulletProgress, BarProgress, StampIcon,
};
})();
