<script lang="ts">
  /**
   * The profile page — the whole point of the exercise.
   *
   * Left rail: photo, vitals, contacting box, URL, interests, details.
   * Main column: latest blog entries, blurbs, friend space.
   *
   * The account's own published CSS (a profile field named `css`) is applied
   * while this page is mounted and torn down on unmount, scoped to
   * `.profile-page` — see lib/stores/theme.svelte.ts.
   */
  import { untrack } from 'svelte'
  import type { Account, Relationship, Status } from '$lib/api/types'
  import { session } from '$lib/stores/session.svelte'
  import { Feed } from '$lib/stores/feed.svelte'
  import { theme, profileCssFromFields } from '$lib/stores/theme.svelte'
  import {
    fetchAccountStatuses,
    fetchFollowers,
    fetchRelationship,
    lookupAccount,
  } from '$lib/api/endpoints'
  import {
    buildProfileView,
    displayNameOf,
    followerCountHidden,
    followersHidden,
    followingCountHidden,
    formatCount,
  } from '$lib/util/profile'
  import { toPlainText } from '$lib/util/html'
  import Module from '$components/common/Module.svelte'
  import ProfileIdentity from '$components/profile/ProfileIdentity.svelte'
  import ContactBox from '$components/profile/ContactBox.svelte'
  import InterestsTable from '$components/profile/InterestsTable.svelte'
  import DetailsTable from '$components/profile/DetailsTable.svelte'
  import FriendSpace from '$components/profile/FriendSpace.svelte'
  import BlogEntry from '$components/blog/BlogEntry.svelte'
  import Pager from '$components/common/Pager.svelte'

  interface Props {
    acct: string
    /** Which sub-page: the profile itself, or a full list. */
    view?: 'profile' | 'blog' | 'friends' | 'pics'
  }

  let { acct, view = 'profile' }: Props = $props()

  let account = $state<Account | null>(null)
  let relationship = $state<Relationship | null>(null)
  let loading = $state(true)
  let error = $state<string | null>(null)

  let friends = $state<Account[]>([])
  let friendsLoading = $state(false)

  // Recreated whenever the account changes, so the feed never shows one
  // person's entries under another's name.
  let entries = $state<Feed<Status>>(new Feed<Status>(async () => ({ items: [], links: {} })))

  const profile = $derived(account ? buildProfileView(account) : null)
  const firstName = $derived(account ? displayNameOf(account).split(/\s+/)[0] : '')
  const listHidden = $derived(account ? followersHidden(account) : false)
  const countHidden = $derived(account ? followerCountHidden(account) : false)
  const followsCountHidden = $derived(account ? followingCountHidden(account) : false)
  /** Route prefix for this profile; snippets can't see the null-narrowing. */
  const base = $derived(account ? `#/@${account.acct}` : '#/')

  /** How many friend tiles the compact grid shows before "[view all]". */
  const FRIEND_PREVIEW = 12

  $effect(() => {
    // Track the inputs that change what's on screen; the load itself is
    // untracked so reading state inside it can't retrigger this effect.
    const handle = acct
    const currentView = view
    const host = session.host
    if (!host) return

    untrack(() => void load(handle, currentView))
  })

  async function load(handle: string, currentView: Props['view']): Promise<void> {
    loading = true
    error = null
    account = null
    relationship = null
    friends = []

    try {
      const found = await lookupAccount(session.api, handle)
      // A newer navigation won the race.
      if (acct !== handle) return

      account = found
      document.title = `${displayNameOf(found)} | plspace`

      theme.applyProfileCss(profileCssFromFields(found.fields))

      entries = new Feed<Status>(
        (cursor) =>
          fetchAccountStatuses(session.api, found.id, cursor, {
            // The profile page mirrors "Latest Blog Entries": top-level posts.
            exclude_replies: currentView !== 'blog',
          }),
        currentView === 'blog' ? 20 : 10,
      )
      void entries.reload()

      void loadFriends(found, currentView)
      void loadRelationship(found)
    } catch (cause) {
      if (acct !== handle) return
      error = cause instanceof Error ? cause.message : 'Could not load that profile.'
    } finally {
      if (acct === handle) loading = false
    }
  }

  async function loadFriends(target: Account, currentView: Props['view']): Promise<void> {
    if (followersHidden(target)) return
    friendsLoading = true
    try {
      const page = await fetchFollowers(session.api, target.id, {
        limit: currentView === 'friends' ? 40 : FRIEND_PREVIEW,
      })
      if (account?.id === target.id) friends = page.items
    } catch {
      // Hidden or unavailable follower lists are normal; the count still shows.
      if (account?.id === target.id) friends = []
    } finally {
      friendsLoading = false
    }
  }

  async function loadRelationship(target: Account): Promise<void> {
    try {
      const found = await fetchRelationship(session.api, target.id)
      if (account?.id === target.id) relationship = found
    } catch {
      /* relationships need auth; absence is fine */
    }
  }

  $effect(() => () => theme.clearProfileCss())

  /** One line of an entry, for the headline list on the profile page. */
  function teaserFor(entry: Status): string {
    const text = toPlainText(entry.spoiler_text || entry.content)
    if (text) return text.length > 110 ? `${text.slice(0, 110).trimEnd()}…` : text
    if (entry.media_attachments.length > 0) {
      const count = entry.media_attachments.length
      return `(${count} photo${count === 1 ? '' : 's'})`
    }
    return '(no text)'
  }
</script>

<div class="page profile-page" data-account={account?.acct ?? acct} data-view={view}>
  {#if loading}
    <p class="loading-note">Loading profile&hellip;</p>
  {:else if error}
    <p class="error-note" role="alert">
      <strong class="error-note-title">Profile not found.</strong>
      {error}
    </p>
  {:else if account && profile}
    <h1 class="page-title profile-name">{displayNameOf(account)}</h1>

    {#if account.moved}
      <p class="profile-moved">
        This account has moved to
        <a href={`#/@${account.moved.acct}`}>@{account.moved.acct}</a>.
      </p>
    {/if}

    <div class="layout--split">
      <div class="layout-column layout-column--left">
        <ProfileIdentity {profile} />

        <ContactBox
          {account}
          {relationship}
          onrelationship={(next) => (relationship = next)}
        />

        <Module title="plspace URL">
          <p class="profile-url">
            <a href={`#/@${account.acct}`}>{location.origin}{location.pathname}#/@{account.acct}</a>
          </p>
        </Module>

        <InterestsTable title={`${firstName}'s Interests`} interests={profile.interests} />
        <DetailsTable title={`${firstName}'s Details`} fields={profile.details} />

        <Module title={`${firstName}'s Stats`} flush>
          <table class="data-table stats-table">
            <tbody>
              <tr>
                <th class="data-table-label" scope="row">Blog entries</th>
                <td class="data-table-value">{formatCount(account.statuses_count)}</td>
              </tr>
              <!-- Pleroma zeroes these counts when the user hides them, so the
                   flag has to be checked before the number is believed. -->
              <tr>
                <th class="data-table-label" scope="row">Friends</th>
                <td class="data-table-value" data-private={countHidden ? 'true' : 'false'}>
                  {#if countHidden}
                    <span class="muted">private</span>
                  {:else}
                    <a href={`#/@${account.acct}/friends`}>{formatCount(account.followers_count)}</a>
                  {/if}
                </td>
              </tr>
              <tr>
                <th class="data-table-label" scope="row">Friend of</th>
                <td class="data-table-value" data-private={followsCountHidden ? 'true' : 'false'}>
                  {#if followsCountHidden}
                    <span class="muted">private</span>
                  {:else}
                    {formatCount(account.following_count)}
                  {/if}
                </td>
              </tr>
              <tr>
                <th class="data-table-label" scope="row">Joined</th>
                <td class="data-table-value">{new Date(account.created_at).getFullYear()}</td>
              </tr>
            </tbody>
          </table>
        </Module>
      </div>

      <div class="layout-column layout-column--main">
        {#if view === 'friends'}
          <FriendSpace
            title={`${firstName}'s Friend Space`}
            ownerName={firstName}
            {friends}
            total={account.followers_count}
            viewAllHref={`#/@${account.acct}`}
            loading={friendsLoading}
            hidden={listHidden}
            {countHidden}
          />
        {:else if view === 'blog'}
          <Module title={`${firstName}'s Blog`} variant="band">
            {#snippet action()}
              <a href={base}>[Back to Profile]</a>
            {/snippet}

            <ul class="blog-list">
              {#each entries.items as status (status.id)}
                <li class="blog-list-item">
                  <BlogEntry
                    {status}
                    compact
                    longFormDate
                    onupdate={(next) => entries.update(status.id, () => next)}
                    ondelete={(id) => entries.remove(id)}
                  />
                </li>
              {/each}
            </ul>

            <Pager
              feed={entries}
              emptyText="There are no Blog Entries yet."
              endText="That’s the whole blog."
            />
          </Module>
        {:else if view === 'pics'}
          <Module title={`${firstName}'s Pics`} variant="band">
            <p class="empty-note">
              Photos appear here as they're attached to blog entries.
              <a href={`#/@${account.acct}/blog`}>Read the blog</a> to see them in context.
            </p>
          </Module>
        {:else}
          <!--
            The profile page lists entry headlines with "(view more)", exactly
            as the 2005 page did. Full entries live on /blog — otherwise ten
            posts of media push the Blurbs and Friend Space off the bottom,
            which is the wrong shape for a profile.
          -->
          <Module title={`${firstName}'s Latest Blog Entries`} variant="band">
            {#snippet action()}
              <a href={`${base}/blog`}>[View Blog]</a>
            {/snippet}

            {#if entries.loading && entries.items.length === 0}
              <p class="loading-note">Loading&hellip;</p>
            {:else if entries.items.length === 0}
              <p class="empty-note">There are no Blog Entries yet.</p>
            {:else}
              <ul class="entry-teaser-list">
                {#each entries.items.slice(0, 6) as status (status.id)}
                  {@const entry = status.reblog ?? status}
                  <li class="entry-teaser" data-status-id={entry.id}>
                    <span class="entry-teaser-text">{teaserFor(entry)}</span>
                    <a class="entry-teaser-link" href={`#/blog/${entry.id}`}>(view more)</a>
                  </li>
                {/each}
              </ul>
              <p class="entry-teaser-all">
                <a href={`${base}/blog`}>[View All Blog Entries]</a>
              </p>
            {/if}
          </Module>

          <Module title={`${firstName}'s Blurbs`} variant="band">
            <h3 class="section-heading">About me:</h3>
            {#if profile.about}
              <div class="rich-text blurb-body">
                <!-- eslint-disable-next-line svelte/no-at-html-tags -- sanitized in buildProfileView -->
                {@html profile.about}
              </div>
            {:else}
              <p class="empty-note">{firstName} hasn't written an About me yet.</p>
            {/if}

            <h3 class="section-heading">Who I'd like to meet:</h3>
            {#if profile.wantsToMeet}
              <div class="rich-text blurb-body">
                <!-- eslint-disable-next-line svelte/no-at-html-tags -- sanitized in buildProfileView -->
                {@html profile.wantsToMeet}
              </div>
            {:else}
              <p class="blurb-body">
                People who educate, inspire or entertain me. And you, apparently.
              </p>
            {/if}
          </Module>

          <FriendSpace
            title={`${firstName}'s Friend Space`}
            ownerName={firstName}
            friends={friends.slice(0, FRIEND_PREVIEW)}
            total={account.followers_count}
            viewAllHref={`#/@${account.acct}/friends`}
            loading={friendsLoading}
            hidden={listHidden}
            {countHidden}
            compact
          />
        {/if}
      </div>
    </div>
  {/if}
</div>
