// Chat panel — message bubbles, day dividers, input with send button.

// Stories Phase 1: the bottom bar is now a PLAIN chat message field — the
// done/not-done controls moved into the check-in sheet (see live.jsx). `mediaUrls`
// maps a check-in's media_path → a signed URL so event blocks can show a
// thumbnail; tapping one calls onOpenStory(subjectUser, day) to open the viewer.
function ChatPanel({ messages, members, onSend, flashMessage, actionWord = 'did it', mediaUrls = {}, onOpenStory, onMemberTap, storyUnseen }) {
  const scrollRef = React.useRef(null);
  const [draft, setDraft] = React.useState('');

  React.useEffect(() => {
    if (scrollRef.current) {
      scrollRef.current.scrollTop = scrollRef.current.scrollHeight;
    }
  }, [messages.length, flashMessage]);

  // Normal chat send.
  const handleSend = () => {
    const v = draft.trim();
    if (!v) return;
    onSend(v);
    setDraft('');
  };

  return (
    <div style={{ display: 'flex', flexDirection: 'column', flex: 1, minHeight: 0 }}>
      <div ref={scrollRef} style={{
        flex: 1, overflowY: 'auto',
        padding: '14px 16px 8px',
        display: 'flex', flexDirection: 'column', gap: 6,
      }}>
        {messages.map((m, i) => {
          const prev = messages[i - 1];
          // WhatsApp-style day separator whenever the calendar day changes.
          const newDay = !prev || !sameDayTs(prev.ts, m.ts);
          return (
            <React.Fragment key={m.id}>
              {newDay && <DayDivider ts={m.ts} />}
              <Message msg={m} prev={prev} next={messages[i + 1]} members={members} actionWord={actionWord} mediaUrls={mediaUrls} onOpenStory={onOpenStory} onMemberTap={onMemberTap} storyUnseen={storyUnseen} />
            </React.Fragment>
          );
        })}
      </div>

      <div style={{
        borderTop: `1px solid ${BORDER}`,
        padding: '10px 12px calc(12px + env(safe-area-inset-bottom))',
        display: 'flex', gap: 8, alignItems: 'center',
        background: '#fbf7ee',
      }}>
        <div style={{
          flex: 1, display: 'flex', alignItems: 'center',
          background: '#fff', borderRadius: 22,
          border: `1px solid ${BORDER}`,
          padding: '0 14px', height: 42,
        }}>
          <input
            value={draft}
            onChange={(e) => setDraft(e.target.value)}
            onKeyDown={(e) => { if (e.key === 'Enter') handleSend(); }}
            placeholder="Type a message…"
            style={{
              flex: 1, border: 'none', outline: 'none', background: 'transparent',
              fontSize: 16, fontFamily: 'inherit', color: INK,
            }}
          />
        </div>
        <button onClick={handleSend} disabled={!draft.trim()} aria-label="Send" style={{
          width: 42, height: 42, borderRadius: '50%',
          border: `1px solid ${draft.trim() ? GREEN : BORDER}`,
          background: draft.trim() ? GREEN : '#fff', color: draft.trim() ? '#fff' : INK_SOFT,
          display: 'flex', alignItems: 'center', justifyContent: 'center',
          cursor: draft.trim() ? 'pointer' : 'default', transition: 'all 0.2s', padding: 0, flexShrink: 0 }}>
          <svg width="18" height="18" viewBox="0 0 18 18" fill="none">
            <path d="M3 9 L15 9 M10 4 L15 9 L10 14" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round" />
          </svg>
        </button>
      </div>
    </div>
  );
}

// A tappable media thumbnail for a check-in event (chat block / history row).
// Photos render as <img>; videos render the first frame via <video> + a ▶ badge.
function StoryThumb({ url, type, size = 44, onClick }) {
  return (
    <button onClick={onClick} aria-label="Open story" style={{
      width: size, height: size, borderRadius: 10, overflow: 'hidden', position: 'relative',
      flexShrink: 0, padding: 0, border: '1px solid rgba(0,0,0,0.12)', background: '#000', cursor: 'pointer' }}>
      {url ? (
        type === 'video'
          ? <video src={url} muted playsInline preload="metadata" style={{ width: '100%', height: '100%', objectFit: 'cover', display: 'block' }} />
          : <img src={url} alt="" style={{ width: '100%', height: '100%', objectFit: 'cover', display: 'block' }} />
      ) : (
        <div style={{ width: '100%', height: '100%', display: 'flex', alignItems: 'center', justifyContent: 'center', background: '#ece7db' }}>
          <div className="spinner" style={{ width: 16, height: 16 }} />
        </div>
      )}
      {type === 'video' && (
        <div style={{ position: 'absolute', inset: 0, display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
          <div style={{ width: 18, height: 18, borderRadius: '50%', background: 'rgba(0,0,0,0.5)', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
            <svg width="9" height="9" viewBox="0 0 10 10" fill="#fff"><path d="M2 1 L9 5 L2 9 Z" /></svg>
          </div>
        </div>
      )}
    </button>
  );
}

function Message({ msg, prev, next, members, actionWord = 'did it', mediaUrls = {}, onOpenStory, onMemberTap, storyUnseen }) {
  const isYou = msg.from === 'you';
  const author = isYou ? null : members.find(m => m.id === msg.from);
  // WhatsApp-style grouping for plain chat bubbles: a "burst" = consecutive
  // messages from the same sender, same day, within 5 minutes. The avatar + name
  // show at the START of a burst; the time shows at the END of a burst (or when
  // the sender / minute / day changes) — never on random middle bubbles.
  const BURST_MS = 5 * 60 * 1000;
  const contiguous = (a, b) => !!a && !!b && a.kind === 'user' && b.kind === 'user'
    && a.from === b.from && sameDayTs(a.ts, b.ts);
  const isBurstStart = !contiguous(prev, msg) || (msg.ts - prev.ts) > BURST_MS;
  const isBurstEnd = !contiguous(msg, next) || (next.ts - msg.ts) > BURST_MS
    || Math.floor(next.ts / 60000) !== Math.floor(msg.ts / 60000);

  if (msg.kind === 'system') {
    return (
      <div style={{
        alignSelf: 'center',
        fontSize: 12, color: INK_SOFT,
        padding: '6px 10px',
        textAlign: 'center', maxWidth: '85%',
      }}>
        {msg.text}
      </div>
    );
  }

  // A "done" event: a single highlighted card with the person's photo + optional
  // note. It's derived from the check-in itself, so undoing the check-in removes
  // this whole block (no "marked / undid" spam).
  if (msg.kind === 'done') {
    const a = members.find(m => m.id === msg.from);
    // B5: the "did it" block uses the person's own colour (green for you) so you
    // can tell at a glance WHO checked in.
    const c = (a && a.color) || GREEN;
    return (
      <div style={{
        alignSelf: isYou ? 'flex-end' : 'flex-start', maxWidth: '84%', margin: '6px 0',
        animation: msg.fresh ? 'slideUp 0.35s cubic-bezier(.2,.8,.2,1)' : 'none',
      }}>
        <div style={{
          display: 'flex', gap: 10, alignItems: 'center',
          background: c + '1e', border: `1px solid ${c}55`, borderLeft: `3px solid ${c}`,
          borderRadius: 16, padding: '10px 13px',
        }}>
          {a && (() => {
            // Per-message pulse: this check-in's media is unseen by the viewer.
            const unseen = !!(msg.media_path && storyUnseen && storyUnseen(msg.subjectUser, msg.day));
            const tap = () => { if (unseen) { onOpenStory && onOpenStory(msg.subjectUser, msg.day); } else { onMemberTap && onMemberTap(a); } };
            return (
              <button onClick={tap} aria-label={`${a.name} story`} style={{ border: 'none', background: 'transparent', padding: 0, cursor: 'pointer', flexShrink: 0, borderRadius: '50%' }}>
                <Avatar variant={a.variant} size={36} ring="green" pulse={unseen} />
              </button>
            );
          })()}
          <div style={{ minWidth: 0, flex: 1 }}>
            <div style={{ fontSize: 14, fontWeight: 700, color: c, display: 'flex', alignItems: 'center', gap: 6 }}>
              <svg width="14" height="14" viewBox="0 0 18 18" fill="none"><path d="M3.5 9.5 L7.5 13 L14.5 5.5" stroke={c} strokeWidth="2.4" strokeLinecap="round" strokeLinejoin="round" /></svg>
              {(a ? a.name : 'Someone')} {actionWord}{msg.dayLabel ? ` · ${msg.dayLabel}` : ''}
            </div>
            {msg.text ? <div style={{ fontSize: 14, color: INK, marginTop: 3, lineHeight: 1.35, wordBreak: 'break-word' }}>{msg.text}</div> : null}
          </div>
          {msg.media_path && (
            <StoryThumb url={mediaUrls[msg.media_path]} type={msg.media_type} size={46}
              onClick={() => onOpenStory && onOpenStory(msg.subjectUser, msg.day)} />
          )}
        </div>
      </div>
    );
  }

  // A "missed" event: greyed block (didn't do it) + optional reason. Also removed
  // when the check-in record is cleared.
  if (msg.kind === 'missed') {
    const a = members.find(m => m.id === msg.from);
    return (
      <div style={{
        alignSelf: isYou ? 'flex-end' : 'flex-start', maxWidth: '84%', margin: '6px 0',
        animation: msg.fresh ? 'slideUp 0.35s cubic-bezier(.2,.8,.2,1)' : 'none',
      }}>
        <div style={{
          display: 'flex', gap: 10, alignItems: 'center',
          background: '#efeadf', border: '1px solid #ddd6c7',
          borderRadius: 16, padding: '10px 13px',
        }}>
          {a && (
            <button onClick={() => { if (msg.media_path) { onOpenStory && onOpenStory(msg.subjectUser, msg.day); } else { onMemberTap && onMemberTap(a); } }}
              aria-label={`${a.name} story`} style={{ border: 'none', background: 'transparent', padding: 0, cursor: 'pointer', flexShrink: 0, borderRadius: '50%' }}>
              <Avatar variant={a.variant} size={36} dim />
            </button>
          )}
          <div style={{ minWidth: 0, flex: 1 }}>
            <div style={{ fontSize: 14, fontWeight: 700, color: INK_SOFT, display: 'flex', alignItems: 'center', gap: 6 }}>
              <svg width="13" height="13" viewBox="0 0 14 14" fill="none"><path d="M3 3 L11 11 M11 3 L3 11" stroke={INK_SOFT} strokeWidth="2" strokeLinecap="round" /></svg>
              {(a ? a.name : 'Someone')} missed it{msg.dayLabel ? ` · ${msg.dayLabel}` : ''}
            </div>
            {msg.text ? <div style={{ fontSize: 14, color: INK, marginTop: 3, lineHeight: 1.35, wordBreak: 'break-word' }}>{msg.text}</div> : null}
          </div>
          {msg.media_path && (
            <StoryThumb url={mediaUrls[msg.media_path]} type={msg.media_type} size={46}
              onClick={() => onOpenStory && onOpenStory(msg.subjectUser, msg.day)} />
          )}
        </div>
      </div>
    );
  }

  return (
    <div style={{
      display: 'flex',
      flexDirection: isYou ? 'row-reverse' : 'row',
      alignItems: 'flex-end',
      gap: 8,
      marginTop: isBurstStart ? 8 : 1,
      animation: msg.fresh ? 'slideUp 0.35s cubic-bezier(.2,.8,.2,1)' : 'none',
    }}>
      {!isYou && (
        <div style={{ width: 30, flexShrink: 0 }}>
          {isBurstStart && author && (
            // Stories Phase 2: sender avatar carries the did-it ring (+ pulse for an
            // unseen story) and taps open the story, or the person's history if none.
            <AvatarWithStreak member={author} size={30} showBadge={false}
              onClick={onMemberTap ? () => onMemberTap(author) : undefined} />
          )}
        </div>
      )}
      <div style={{
        display: 'flex', flexDirection: 'column',
        alignItems: isYou ? 'flex-end' : 'flex-start',
        maxWidth: '75%',
      }}>
        {/* B5 Variant C: others = neutral bubble + coloured name on top + a thin
            left stripe in their colour; you = right-aligned green. */}
        <div style={{
          background: isYou ? '#cde4d7' : '#fff',
          color: INK,
          padding: '8px 12px',
          borderRadius: 16,
          borderBottomRightRadius: isYou ? 4 : 16,
          borderBottomLeftRadius: isYou ? 16 : 4,
          borderLeft: isYou ? 'none' : `3px solid ${(author && author.color) || GREEN}`,
          boxShadow: '0 1px 0 rgba(0,0,0,0.05)',
          fontSize: 15, lineHeight: 1.35,
          wordBreak: 'break-word',
        }}>
          {!isYou && isBurstStart && author && (
            <div style={{ fontSize: 11.5, fontWeight: 800, color: author.color || GREEN, marginBottom: 2 }}>{author.name}</div>
          )}
          {msg.text}
        </div>
        {isBurstEnd && (
          <div style={{
            fontSize: 11, color: INK_SOFT, marginTop: 2,
            padding: '0 4px',
          }}>
            {timeTs(msg.ts)}
          </div>
        )}
      </div>
    </div>
  );
}

// ── WhatsApp-style time / day helpers ───────────────────────────────────────
function sameDayTs(a, b) {
  const da = new Date(a), db = new Date(b);
  return da.getFullYear() === db.getFullYear() && da.getMonth() === db.getMonth() && da.getDate() === db.getDate();
}
function timeTs(ts) {
  const d = new Date(ts);
  return `${d.getHours().toString().padStart(2, '0')}:${d.getMinutes().toString().padStart(2, '0')}`;
}
// Centered date pill shown between days (Today / Yesterday / weekday / full date).
function DayDivider({ ts }) {
  const d = new Date(ts);
  const startOf = (x) => new Date(x.getFullYear(), x.getMonth(), x.getDate()).getTime();
  const diff = Math.round((startOf(new Date()) - startOf(d)) / 86400000);
  const days = ['Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday'];
  const mon = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'];
  const label = diff === 0 ? 'Today' : diff === 1 ? 'Yesterday'
    : diff > 1 && diff < 7 ? days[d.getDay()]
    : `${mon[d.getMonth()]} ${d.getDate()}${d.getFullYear() !== new Date().getFullYear() ? ', ' + d.getFullYear() : ''}`;
  return (
    <div style={{ alignSelf: 'center', margin: '8px 0 4px' }}>
      <span style={{ fontSize: 11.5, fontWeight: 700, color: INK_SOFT, background: '#efeadf',
        borderRadius: 100, padding: '3px 12px', boxShadow: '0 1px 0 rgba(0,0,0,0.04)' }}>{label}</span>
    </div>
  );
}

Object.assign(window, { ChatPanel, StoryThumb });
