<script lang="ts">
  /**
   * "Hello, Tom!" — the logged-in dashboard, modelled on the 2007 home page:
   * a narrow left rail with the control panel, a main column of Friend Status
   * and Bulletin Space, and a right rail of server odds and ends.
   *
   * Logged out, the same page becomes a front door: what this server is, and
   * what its local timeline looks like right now.
   */
  import type { Account, Notification, Status } from '$lib/api/types'
  import { useAppServices } from '$lib/app-services'
  import {
    instanceDomain,
    instanceStats,
    instanceThumbnail,
  } from '$lib/api/endpoints'
  import { displayNameOf, fallbackMood, formatCount, profilePath } from '$lib/util/profile'
  import { escapeHtml, toPlainText } from '$lib/util/html'
  import { relativeTime, shortDate, stampDate } from '$lib/util/time'
  import { useTimelineRefresh } from '$lib/timeline-refresh'
  import { reconcileRefreshItems } from '$lib/stores/feed.svelte'
  import Module from '$components/common/Module.svelte'
  import Avatar from '$components/common/Avatar.svelte'
  import EmojiText from '$components/common/EmojiText.svelte'
  import RichText from '$components/common/RichText.svelte'
  import MfmContent from '$components/common/MfmContent.svelte'
  import Composer from '$components/blog/Composer.svelte'

  const { endpoints, session } = useAppServices()
  const timelineRefresh = useTimelineRefresh()

  let friendStatus = $state<Status[]>([])
  let bulletins = $state<Status[]>([])
  let following = $state<Account[]>([])
  let notifications = $state<Notification[]>([])
  let loading = $state(true)
  let error = $state<string | null>(null)
  /**
   * Per-module failures. Several large servers (mastodon.social among them)
   * refuse timeline reads without a token, so "empty" and "not allowed" must
   * look different or the page reads as broken.
   */
  let friendStatusError = $state<string | null>(null)
  let bulletinError = $state<string | null>(null)
  let loadGeneration = 0
  let primaryRefreshGeneration = 0

  const me = $derived(session.me)
  const domain = $derived(session.host ? instanceDomain(session.instance, session.host) : '')
  const thumbnail = $derived(instanceThumbnail(session.instance))
  const stats = $derived(instanceStats(session.instance))

  /** Notification counts by kind, for the "New Messages!" summary. */
  const counts = $derived.by(() => {
    const table: Record<string, number> = {}
    for (const notification of notifications) {
      table[notification.type] = (table[notification.type] ?? 0) + 1
    }
    return table
  })

  const summary = $derived([
    { label: 'New Messages!', count: counts.mention ?? 0, href: '#/mail/mentions', kind: 'mention' },
    { label: 'New Friend Requests!', count: counts.follow_request ?? 0, href: '#/mail/requests', kind: 'follow_request' },
    { label: 'New Friends!', count: counts.follow ?? 0, href: '#/mail/follows', kind: 'follow' },
    { label: 'New Kudos!', count: counts.favourite ?? 0, href: '#/mail/kudos', kind: 'favourite' },
    { label: 'New Reposts!', count: counts.reblog ?? 0, href: '#/mail/reposts', kind: 'reblog' },
  ])

  $effect(() => {
    const host = session.host
    const signedIn = session.signedIn
    if (!host) {
      loadGeneration += 1
      loading = false
      return
    }
    const generation = ++loadGeneration
    void load(signedIn, host, generation)
    return () => {
      if (generation === loadGeneration) loadGeneration += 1
    }
  })

  $effect(() => {
    const host = session.host
    const signedIn = session.signedIn
    if (!timelineRefresh || !host) return
    return timelineRefresh.register(() => void refreshPrimaryTimeline(signedIn, host))
  })

  async function load(signedIn: boolean, host = session.host, generation = ++loadGeneration): Promise<void> {
    loading = true
    error = null
    friendStatusError = null
    bulletinError = null

    try {
      // Everything here is independent; a failure in one shouldn't blank the page.
      const [statusPage, bulletinPage] = await Promise.all([
        signedIn
          ? endpoints.fetchTimeline(session.api, 'home', { limit: 10 }).catch((cause) => {
              if (generation === loadGeneration) friendStatusError = messageOf(cause)
              return { items: [], links: {} }
            })
          : Promise.resolve({ items: [], links: {} }),
        endpoints.fetchTimeline(session.api, 'local', { limit: 10 }).catch((cause) => {
          if (generation === loadGeneration) bulletinError = messageOf(cause)
          return { items: [], links: {} }
        }),
      ])

      if (generation !== loadGeneration || session.host !== host) return
      friendStatus = statusPage.items
      bulletins = bulletinPage.items

      if (signedIn && session.me) {
        const accountId = session.me.id
        void endpoints
          .fetchFollowing(session.api, accountId, { limit: 12 })
          .then((page) => {
            if (generation === loadGeneration) following = page.items
          })
          .catch(() => {
            if (generation === loadGeneration) following = []
          })
        void endpoints
          .fetchNotifications(session.api, { limit: 40 })
          .then((page) => {
            if (generation === loadGeneration) notifications = page.items
          })
          .catch(() => {
            if (generation === loadGeneration) notifications = []
          })
      } else {
        following = []
        notifications = []
      }
    } catch (cause) {
      if (generation === loadGeneration) {
        error = cause instanceof Error ? cause.message : 'Could not load your home page.'
      }
    } finally {
      if (generation === loadGeneration) loading = false
    }
  }

  /** Refresh only the dashboard's visible primary timeline, not its sidebars. */
  async function refreshPrimaryTimeline(signedIn: boolean, host: string): Promise<void> {
    const generation = ++primaryRefreshGeneration

    if (signedIn) {
      friendStatusError = null
      try {
        const page = await endpoints.fetchTimeline(session.api, 'home', { limit: 10 })
        if (
          generation === primaryRefreshGeneration &&
          session.host === host &&
          session.signedIn === signedIn
        ) {
          friendStatus = reconcileRefreshItems(
            friendStatus,
            page.items,
            page.deletedIds,
            { emptyIsAuthoritative: !page.links.maxId },
          )
        }
      } catch (cause) {
        if (generation === primaryRefreshGeneration && session.host === host) {
          friendStatusError = messageOf(cause)
        }
      }
      return
    }

    bulletinError = null
    try {
      const page = await endpoints.fetchTimeline(session.api, 'local', { limit: 10 })
      if (
        generation === primaryRefreshGeneration &&
        session.host === host &&
        session.signedIn === signedIn
      ) {
        bulletins = reconcileRefreshItems(
          bulletins,
          page.items,
          page.deletedIds,
          { emptyIsAuthoritative: !page.links.maxId },
        )
      }
    } catch (cause) {
      if (generation === primaryRefreshGeneration && session.host === host) {
        bulletinError = messageOf(cause)
      }
    }
  }

  function moodFor(status: Status): string {
    return fallbackMood(status.account.id)
  }

  function messageOf(cause: unknown): string {
    return cause instanceof Error ? cause.message : 'Could not load that.'
  }
</script>

<div class="page home-page">
  {#if !session.host}
    <h1 class="page-title">Welcome to plspace</h1>
    <p class="page-subtitle">It&rsquo;s always Pleroma&trade;.</p>
    <div class="layout--single">
      <Module title="Get started">
        <p>
          plspace is a web client for Pleroma. Point it at your server to begin.
        </p>
        <p><a class="button button--primary" href="#/login">Choose your server</a></p>
      </Module>
    </div>
  {:else}
    <h1 class="page-title">
      {#if me}
        Hello, <EmojiText text={displayNameOf(me).split(/\s+/)[0]} emojis={me.emojis} />!
      {:else}
        {domain}
      {/if}
    </h1>
    {#if me}
      <p class="page-subtitle">
        My URL: <a href={profilePath(me)}>#/@{me.acct}</a>
        &nbsp;&middot;&nbsp; Last login: {shortDate(me.last_status_at ?? new Date())}
      </p>
    {:else}
      <p class="page-subtitle">
        Browsing as a guest. <a href="#/login">Sign in</a> to post, follow and read your own feed.
      </p>
    {/if}

    {#if error}
      <p class="error-note" role="alert">{error}</p>
    {/if}

    <div class="layout--dashboard">
      <!-- ------------------------------------------------ left: control panel -->
      <div class="layout-column layout-column--left">
        {#if me}
          <Module title="My Profile">
            <p class="center">
              <Avatar account={me} size="friend" />
            </p>
            <p class="center">
              <a href={profilePath(me)}>
                <EmojiText text={displayNameOf(me)} emojis={me.emojis} />
              </a>
            </p>
            <p class="center muted">
              Profile views: {formatCount(me.statuses_count)} entries
            </p>
          </Module>

          <Module title="Updates">
            <ul class="mail-summary">
              {#each summary as item (item.kind)}
                <li class="mail-summary-item" data-kind={item.kind} data-unread={item.count > 0 ? 'true' : 'false'}>
                  <a href={item.href}>{item.label}</a>
                  {#if item.count > 0}<span class="mail-folder-count">({item.count})</span>{/if}
                </li>
              {/each}
            </ul>
            <div class="mail-summary-buttons">
              <a class="button button--small" href="#/mail">inbox</a>
              <a class="button button--small" href="#/mail/requests">friend requests</a>
              <a class="button button--small" href="#/timeline/home">my feed</a>
              <a class="button button--small" href="#/compose">post bulletin</a>
            </div>
          </Module>
        {/if}

        <Module title="Control Panel">
          <ul class="action-list action-list--single">
            <li class="action-list-item">
              <span class="action-list-icon" aria-hidden="true">✎</span>
              <a class="action-list-label" href="#/compose">Compose</a>
            </li>
            <li class="action-list-item">
              <span class="action-list-icon" aria-hidden="true">📥</span>
              <a class="action-list-label" href="#/mail">Inbox</a>
            </li>
            <li class="action-list-item">
              <span class="action-list-icon" aria-hidden="true">🌐</span>
              <a class="action-list-label" href="#/timeline/public">The whole network</a>
            </li>
            <li class="action-list-item">
              <span class="action-list-icon" aria-hidden="true">🔍</span>
              <a class="action-list-label" href="#/browse">Browse people</a>
            </li>
            <li class="action-list-item">
              <span class="action-list-icon" aria-hidden="true">🎨</span>
              <a class="action-list-label" href="#/settings">Layouts &amp; settings</a>
            </li>
          </ul>
        </Module>
      </div>

      <!-- --------------------------------------------------- main: the feed -->
      <div class="layout-column layout-column--main">
        {#if session.signedIn}
          <Module title="Post a bulletin">
            <Composer
              placeholder="What are you up to?"
              submitLabel="Post"
              onposted={() => void load(true)}
            />
          </Module>
        {/if}

        <Module title="Friend Status">
          {#snippet action()}
            <a href="#/timeline/home">view all</a>
          {/snippet}

          {#if !session.signedIn}
            <p class="empty-note"><a href="#/login">Sign in</a> to see what your friends are up to.</p>
          {:else if friendStatusError}
            <p class="error-note" role="alert">{friendStatusError}</p>
          {:else if loading && friendStatus.length === 0}
            <p class="loading-note">Loading&hellip;</p>
          {:else if friendStatus.length === 0}
            <p class="empty-note">Nothing yet. Add some friends to fill this up.</p>
          {:else}
            <ul class="status-line-list">
              {#each friendStatus as status (status.id)}
                {@const entry = status.reblog ?? status}
                <li class="status-line" data-account={entry.account.acct}>
                  <Avatar account={entry.account} />
                  <div class="status-line-body">
                    <a class="status-line-author" href={profilePath(entry.account)}>
                      <EmojiText text={displayNameOf(entry.account)} emojis={entry.account.emojis} />
                    </a>
                    <MfmContent
                      html={entry.spoiler_text ? `<p>${escapeHtml(entry.spoiler_text)}</p>` : entry.content}
                      emojis={entry.emojis}
                      mentions={entry.mentions}
                      tags={entry.tags}
                      inline
                    />
                    <a class="status-line-time" href={`#/blog/${entry.id}`}>
                      {relativeTime(entry.created_at)}
                    </a>
                    <span class="status-line-mood">Mood: {moodFor(entry)}</span>
                  </div>
                </li>
              {/each}
            </ul>
          {/if}
        </Module>

        <Module title="Bulletin Space">
          {#snippet action()}
            <a href="#/timeline/local">view all</a>
          {/snippet}

          {#if bulletinError}
            <p class="error-note" role="alert">
              {bulletinError}
              {#if !session.signedIn}
                <a href="#/login">Signing in</a> usually fixes this &mdash; some servers don't serve
                timelines to guests.
              {/if}
            </p>
          {:else if bulletins.length === 0}
            <p class="empty-note">{loading ? 'Loading…' : 'No bulletins right now.'}</p>
          {:else}
            <table class="bulletin-table">
              <thead>
                <tr>
                  <th scope="col">From</th>
                  <th scope="col">Date</th>
                  <th scope="col">Bulletin</th>
                </tr>
              </thead>
              <tbody>
                {#each bulletins as status (status.id)}
                  {@const entry = status.reblog ?? status}
                  <tr>
                    <td class="bulletin-from">
                      <a href={profilePath(entry.account)}>
                        <EmojiText text={displayNameOf(entry.account)} emojis={entry.account.emojis} />
                      </a>
                    </td>
                    <td class="bulletin-date">{stampDate(entry.created_at)}</td>
                    <td class="bulletin-subject">
                      <a href={`#/blog/${entry.id}`}>
                        <EmojiText
                          text={toPlainText(entry.spoiler_text || entry.content).slice(0, 90) || '(no text)'}
                          emojis={entry.emojis}
                        />
                      </a>
                    </td>
                  </tr>
                {/each}
              </tbody>
            </table>
          {/if}
        </Module>

        {#if session.signedIn}
          <Module title="Friend Space">
            {#snippet action()}
              <a href={me ? `#/@${me.acct}/friends` : '#/browse'}>view all</a>
            {/snippet}

            {#if following.length === 0}
              <p class="empty-note">You haven't added any friends yet. <a href="#/browse">Find some.</a></p>
            {:else}
              <p class="friend-count">
                You have <span class="friend-count-value">{formatCount(me?.following_count ?? 0)}</span> friends.
              </p>
              <ul class="friend-grid friend-grid--compact">
                {#each following as friend (friend.id)}
                  <li class="friend-card" data-account={friend.acct}>
                    <a class="friend-card-link" href={profilePath(friend)}>
                      <EmojiText
                        class="friend-card-name"
                        text={displayNameOf(friend)}
                        emojis={friend.emojis}
                      />
                      <img
                        class="friend-card-photo"
                        src={friend.avatar_static || friend.avatar}
                        alt=""
                        loading="lazy"
                      />
                    </a>
                  </li>
                {/each}
              </ul>
            {/if}
          </Module>
        {/if}
      </div>

      <!-- ------------------------------------------------- right: the server -->
      <div class="layout-column layout-column--right">
        <Module title={domain}>
          {#if thumbnail}
            <p class="center">
              <img class="instance-thumbnail" src={thumbnail} alt="" loading="lazy" />
            </p>
          {/if}
          <p class="instance-title"><strong>{session.instance?.title ?? domain}</strong></p>
          {#if session.instance?.short_description || session.instance?.description}
            <RichText
              html={session.instance.short_description || session.instance.description}
              class="instance-description"
            />
          {/if}
          {#if stats.length > 0}
            <table class="data-table instance-stats">
              <tbody>
                {#each stats as stat (stat.label)}
                  <tr>
                    <th class="data-table-label" scope="row">{stat.label}</th>
                    <td class="data-table-value">{formatCount(stat.value)}</td>
                  </tr>
                {/each}
              </tbody>
            </table>
          {/if}
        </Module>

        <Module title="plspace Tip">
          <p>
            Name a profile field <code>Music</code>, <code>Movies</code>, <code>Books</code> or
            <code>Heroes</code> and it fills in the Interests table on your profile. Name one
            <code>Mood</code> and it shows beside your photo.
          </p>
          <p><a href="#/settings">Customise your layout &rarr;</a></p>
        </Module>
      </div>
    </div>
  {/if}
</div>
