<script lang="ts">
  import EmojiText from './EmojiText.svelte';
  import MessageRow from './MessageRow.svelte';
  import { formatDay } from './format';
  import type { PendingMessage } from './pending';
  import { buildGroups, lastOwnSent, lastSenderIs, pendingToEvent } from './timeline-groups';
  import type { ImagePack, LinkPreview, Person, QuickReaction, RecentHistoryState, ReplyReference, TimelineEvent, TimelineItem } from './types';

  export let items: TimelineItem[];
  export let pending: PendingMessage[] = [];
  export let joined: boolean;
  export let self: string | null;
  export let selfName: string | null = null;
  export let people: Record<string, Person> = {};
  export let senderAvatarFor: (userId: string) => string | null;
  export let reacting: boolean;
  export let quickReactions: QuickReaction[] = [];
  export let packs: ImagePack[] = [];
  export let linkPreviews: Record<string, LinkPreview | null> = {};
  export let loadPreview: ((url: string) => void) | null = null;
  export let editingEventId: string | null = null;
  export let recentState: RecentHistoryState | undefined = undefined;
  export let recentRetrying = false;
  export let paginating = false;
  export let fillingGap: string | null = null;
  export let onReact: (eventId: string, key: string, shortcode?: string) => void;
  export let onQuickChange: (list: QuickReaction[]) => void;
  export let onUser: (userId: string, x: number, y: number, menu: boolean) => void;
  export let onCloneSticker: (eventId: string, body: string, x: number, y: number) => void;
  export let onMore: (event: TimelineEvent, x: number, y: number) => void;
  export let onOpenThread: (rootEventId: string) => void;
  export let onReply: (target: ReplyReference) => void;
  export let onJump: (eventId: string) => void;
  export let onEdit: (eventId: string, body: string) => void;
  export let onPaginate: () => void;
  export let onFillGap: (gapId: string) => void;
  export let onRetryRecent: () => void;
  export let onRetryPending: (id: string) => void;
  export let onDiscardPending: (id: string) => void;

  $: hasStart = items.some((item) => item.kind === 'start');
  $: grouped = buildGroups(items);
  $: lastSentId = lastOwnSent(items);
  $: pendingFollowsSelf = lastSenderIs(items, self);
</script>

<div class="timeline-top">
  {#if recentState === 'Loading'}
    <p class="side-note">Loading recent history…</p>
  {:else if recentState && typeof recentState === 'object'}
    <div class="history-gap">
      <span>Recent history could not be loaded</span>
      <button type="button" class="ghost-btn" disabled={recentRetrying} on:click={onRetryRecent}>
        {recentRetrying ? 'Loading…' : 'Retry'}
      </button>
    </div>
  {/if}
  {#if hasStart}
    <p class="start-note">Beginning of history</p>
  {:else}
    <button type="button" class="ghost-btn" disabled={paginating || recentState === 'Loading'} on:click={onPaginate}>
      {paginating ? 'Loading…' : 'Load older messages'}
    </button>
  {/if}
</div>
{#if grouped.length === 0}
  <p class="side-note timeline-empty">No messages yet</p>
{:else}
  {#each grouped as g (g.key)}
    {#if g.item.kind === 'day'}
      <div class="day-row"><span>{formatDay(g.item.timestamp_ms)}</span></div>
    {:else if g.item.kind === 'readMarker'}
      <div class="new-row"><span>New</span></div>
    {:else if g.item.kind === 'start'}
      <p class="start-note">Beginning of history</p>
    {:else if g.item.kind === 'historyGap'}
      {#if g.item.gap.continuity !== 'Verified'}
        {@const gap = g.item.gap}
        {@const filling = fillingGap === gap.id || gap.lifecycle === 'Fetching'}
        <div class="history-gap">
          <span>{gap.failure ?? 'Some history is not loaded'}</span>
          <button type="button" class="ghost-btn" disabled={!!fillingGap || gap.lifecycle === 'Fetching' || gap.lifecycle === 'Unavailable'} on:click={() => onFillGap(gap.id)}>
            {filling ? 'Loading…' : gap.lifecycle === 'Unavailable' ? 'History unavailable' : 'Fetch older history'}
          </button>
        </div>
      {/if}
    {:else if g.item.kind === 'unknown'}
      <p class="unknown-note">Unsupported event ({g.item.label})</p>
    {:else}
      <MessageRow
        itemId={g.key}
        event={g.item.event}
        showSender={g.showSender}
        senderAvatar={senderAvatarFor(g.item.event.sender.user_id) ?? g.item.event.sender.avatar}
        {reacting}
        {onReact}
        {quickReactions}
        {packs}
        {linkPreviews}
        {loadPreview}
        {onQuickChange}
        {people}
        {self}
        {onUser}
        {onCloneSticker}
        {onMore}
        onOpenThread={joined || g.item.event.thread_summary ? onOpenThread : null}
        onReply={joined ? onReply : null}
        {onJump}
        onEdit={joined ? onEdit : null}
        editing={editingEventId !== null && editingEventId === g.item.event.event_id}
        showSent={g.item.event.item_id === lastSentId && pending.length === 0}
      />
    {/if}
  {/each}
{/if}
{#each pending as p, i (p.id)}
  <MessageRow
    itemId={`pending-${p.id}`}
    event={pendingToEvent(p, self, selfName)}
    {onJump}
    showSender={i === 0 && !pendingFollowsSelf}
    senderAvatar={senderAvatarFor(self ?? '')}
    {reacting}
    {onReact}
    {quickReactions}
    {packs}
    {linkPreviews}
    {loadPreview}
    {onQuickChange}
  />
  {#if p.status === 'failed'}
    <div class="pending-actions">
      <span class="pending-error" role="alert"><EmojiText text={p.error ?? ''} /></span>
      <button type="button" class="ghost-btn" on:click={() => onRetryPending(p.id)}>Retry</button>
      <button type="button" class="ghost-btn danger" on:click={() => onDiscardPending(p.id)}>Discard</button>
    </div>
  {/if}
{/each}
