/* global React, ReactDOM, PARTNER_TIERS, ProgressBar */
// CourseCard — one partner course as a picture, a name, and who holds the keys.
//
// Shared on purpose: the Course Staff grid and the map's hover preview are
// the same card. Two implementations of "a course tile" would drift within a
// week, and the map version would be the one nobody noticed had gone stale.
//
// Built for a dark surface, so it leans on the photograph for warmth and
// keeps the type to the brand faces — display for the name, mono for the
// data lines — rather than adding another accent colour.

// The picture the golfer app shows. `hero_img` is the headline shot and
// `render_img` the drawn flyover; either is better than an empty frame, so
// take whichever exists.
const courseImage = (c) => (c && (c.hero_img || c.render_img)) || null;

// No photograph yet. A broken <img> or a grey box both read as a fault, so
// the gap gets a deliberate brand panel instead — it looks like a decision.
function CourseCardFallback({ label }) {
  return (
    <div style={{
      position: 'absolute', inset: 0,
      display: 'flex', alignItems: 'center', justifyContent: 'center',
      background: 'linear-gradient(150deg, var(--forest-dark, #12301D), var(--forest, #1C492A))',
    }}>
      <img src="assets/monogram-cream.svg" alt="" style={{ height: '38%', opacity: 0.22 }}/>
      {label && (
        <span className="pill-mono" style={{
          position: 'absolute', bottom: 10, fontSize: 8.5, padding: '2px 8px',
          background: 'transparent', color: 'rgba(234,226,206,0.55)',
          border: '1px dashed rgba(234,226,206,0.3)',
        }}>No photo yet</span>
      )}
    </div>
  );
}

// Up to three faces, then a count. Past three the row stops being scannable
// and starts being a wall.
function StaffFaces({ managers }) {
  const shown = managers.slice(0, 3);
  const rest = managers.length - shown.length;
  return (
    <span style={{ display: 'inline-flex', alignItems: 'center' }}>
      {shown.map((m, i) => (
        <span key={m.id || i} title={m.name}
          style={{
            width: 22, height: 22, borderRadius: 999, flexShrink: 0,
            marginLeft: i ? -7 : 0, zIndex: shown.length - i,
            border: '1.5px solid var(--surface)',
            background: 'var(--cream)', color: 'var(--forest)',
            display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
            fontSize: 9, fontWeight: 800, overflow: 'hidden',
          }}>
          {m.avatar_url
            ? <img src={m.avatar_url} alt="" style={{ width: '100%', height: '100%', objectFit: 'cover' }}/>
            : m.initials}
        </span>
      ))}
      {rest > 0 && (
        <span style={{ marginLeft: 6, fontSize: 11, fontFamily: 'var(--font-mono)', color: 'var(--ink-muted)' }}>
          +{rest}
        </span>
      )}
    </span>
  );
}

// ─── CourseCard ───────────────────────────────────────────────────────
// managers: [{ id, name, initials, avatar_url }]
//
// flagNonPartner: whether a course with no Sandbox 9 wears a "Non-partner"
// pill. On the Course Managers grid and its detail panel — the only other
// two places this card is used — partners are the norm and the exception is
// worth calling out. On the Courses page, which lists every course we hold a
// card for, the exception is the majority: the pill lands on most of the grid
// and says nothing the absent monogram has not already said. That page turns
// it off.
// onboarding: { pct } when this course is part-way through being onboarded.
// Shown as a thin bar under the name — the one place you see how far along a
// course is without opening it.
function CourseCard({ course, managers = [], onClick, compact = false, selected = false, flagNonPartner = true, onboarding = null }) {
  const [failed, setFailed] = React.useState(false);
  const img = courseImage(course);
  const tier = course.tier && PARTNER_TIERS[course.tier] ? PARTNER_TIERS[course.tier] : null;
  const where = [course.city, course.state].filter(Boolean).join(', ');
  const staffed = managers.length > 0;
  const inactive = course.status && course.status !== 'active';

  const body = (
    <>
      {/* Picture */}
      <div style={{
        position: 'relative', width: '100%',
        aspectRatio: compact ? '16 / 9' : '4 / 3',
        overflow: 'hidden', background: 'var(--forest-dark, #12301D)',
      }}>
        {img && !failed
          ? <img src={img} alt="" onError={() => setFailed(true)}
              style={{ width: '100%', height: '100%', objectFit: 'cover', display: 'block' }}/>
          : <CourseCardFallback label={!compact}/>}

        {/* A wash at the foot of the photo so the pills stay readable over
            whatever the image happens to be doing down there. */}
        <div style={{
          position: 'absolute', inset: 0, pointerEvents: 'none',
          background: 'linear-gradient(to bottom, rgba(14,28,19,0.30) 0%, rgba(14,28,19,0) 38%, rgba(14,28,19,0.55) 100%)',
        }}/>

        <div style={{ position: 'absolute', top: 9, left: 10, right: 10, display: 'flex', gap: 6, flexWrap: 'wrap' }}>
          {tier && (
            <span className="pill-mono" style={{
              fontSize: 8.5, padding: '2px 8px',
              background: 'rgba(234,226,206,0.92)', color: 'var(--forest)', border: 'none',
            }}>{tier.label}</span>
          )}
          {course.hasSbx === true && (
            // The monogram is the whole signal on a list that now holds every
            // course we know: this one has a Sandbox 9 laid out on it. Same
            // mark the partner pins carry on the map.
            <span title="On Sandbox — has a Sandbox 9" aria-label="On Sandbox" style={{
              display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
              width: 20, height: 20, borderRadius: 999, flexShrink: 0,
              background: 'var(--forest, #1C492A)',
              boxShadow: '0 0 0 1.5px rgba(234,226,206,0.85)',
            }}>
              <img src="assets/monogram-cream.svg" alt="" style={{ height: 10, display: 'block' }}/>
            </span>
          )}
          {course.hasSbx === false && flagNonPartner && (
            // Same distinction the map pins draw: a course we list so people
            // can play it, with no Sandbox 9 laid out on it.
            <span className="pill-mono" style={{
              fontSize: 8.5, padding: '2px 8px',
              background: 'rgba(14,28,19,0.55)', color: 'rgba(234,226,206,0.85)',
              border: '1px solid rgba(234,226,206,0.35)',
            }}>Non-partner</span>
          )}
          {inactive && (
            <span className="pill-mono" style={{
              fontSize: 8.5, padding: '2px 8px',
              background: 'rgba(155,58,46,0.85)', color: '#FFF', border: 'none',
            }}>{String(course.status).replace('_', ' ')}</span>
          )}
        </div>
      </div>

      {/* Name, place, keys */}
      <div style={{ padding: compact ? '10px 12px 12px' : '13px 15px 15px' }}>
        <div style={{
          fontFamily: 'var(--font-display)', color: 'var(--paper)', lineHeight: 1.15,
          fontSize: compact ? 15 : 17,
          overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap',
        }}>
          {course.short_name || course.name}
        </div>
        {onboarding && (
          <div style={{ display: 'flex', alignItems: 'center', gap: 8, margin: '8px 0 2px' }}
            title={`Onboarding ${onboarding.pct}% complete`}>
            <ProgressBar pct={onboarding.pct} height={4}/>
            <span style={{ fontFamily: 'var(--font-mono)', fontSize: 10, color: 'var(--ink-muted)', flexShrink: 0 }}>
              {onboarding.pct}%
            </span>
          </div>
        )}
        {where && (
          <div style={{
            fontFamily: 'var(--font-mono)', fontSize: 10.5, color: 'var(--ink-muted)',
            marginTop: 3, textTransform: 'uppercase', letterSpacing: '0.05em',
          }}>{where}</div>
        )}

        <div style={{
          display: 'flex', alignItems: 'center', gap: 8, marginTop: compact ? 8 : 11,
          paddingTop: compact ? 8 : 11, borderTop: '1px solid var(--line-soft)',
          minHeight: 26,
        }}>
          {staffed ? (
            <>
              <StaffFaces managers={managers}/>
              <span style={{
                fontSize: 12, color: 'var(--ink)', minWidth: 0,
                overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap',
              }}>
                {managers.length === 1 ? managers[0].name : `${managers.length} with access`}
              </span>
            </>
          ) : (
            // The state that actually matters operationally: nobody here can
            // publish a tee time, so it is called out rather than left blank.
            <span className="pill-mono" style={{
              fontSize: 9, padding: '3px 9px',
              background: 'rgba(155,58,46,0.18)', color: 'var(--loss-soft, #E7B8A7)',
              border: '1px solid rgba(155,58,46,0.5)',
            }}>No manager assigned</span>
          )}
        </div>
      </div>
    </>
  );

  const shell = {
    display: 'block', width: '100%', textAlign: 'left', font: 'inherit',
    padding: 0, overflow: 'hidden',
    border: `1px solid ${selected ? 'var(--cream)' : 'var(--line)'}`,
    borderRadius: 'var(--r-md, 14px)',
    background: 'var(--surface)',
    cursor: onClick ? 'pointer' : 'default',
    transition: 'transform var(--dur-fast, 140ms) var(--ease, ease), border-color var(--dur-fast, 140ms) var(--ease, ease)',
  };

  if (!onClick) return <div className="course-card" style={shell}>{body}</div>;
  return (
    <button type="button" className="course-card" style={shell} onClick={() => onClick(course)}
      aria-label={`${course.short_name || course.name} — ${staffed ? `${managers.length} with access` : 'no manager assigned'}`}>
      {body}
    </button>
  );
}

// ─── Rendering one into a plain DOM node ──────────────────────────────
// Leaflet popups take a DOM element, not JSX. Rather than hand-writing a
// second HTML version of the card — which would drift from this one — the
// real component is mounted into the node, and the root is kept on the node
// so re-hovering the same pin reuses it instead of leaking a root per hover.
function mountCourseCard(node, props) {
  if (!node) return;
  if (!node.__sbxRoot) node.__sbxRoot = ReactDOM.createRoot(node);
  node.__sbxRoot.render(React.createElement(CourseCard, props));
}

Object.assign(window, { CourseCard, courseImage, mountCourseCard });
