<script lang="ts">
  import { onMount } from 'svelte';
  import PlayerCard from '$lib/components/PlayerCard.svelte';

  type GuestbookAuthor = {
    beatleaderId: string;
    name: string | null;
    avatar: string | null;
    country: string | null;
    rank: number | null;
    techPp: number | null;
    accPp: number | null;
    passPp: number | null;
  };

  type GuestbookComment = {
    id: string;
    message: string;
    author: GuestbookAuthor;
    createdAt: number;
    updatedAt: number | null;
  };

  type GuestbookViewer = {
    beatleaderId: string;
    name: string | null;
    avatar: string | null;
  };

  type GuestbookPagination = {
    total: number;
    offset: number;
    limit: number;
    hasPrev: boolean;
    hasNext: boolean;
  };

  type GuestbookResponse = {
    viewer: GuestbookViewer | null;
    comments: GuestbookComment[];
    pagination?: GuestbookPagination | null;
  };

  let loading = false;
  let error: string | null = null;
  let comments: GuestbookComment[] = [];
  let viewer: GuestbookViewer | null = null;
  let newMessage = '';
  let newMessageError: string | null = null;
  let submittingNew = false;
  let pagination: GuestbookPagination = { total: 0, offset: 0, limit: 5, hasPrev: false, hasNext: false };

  const loginUrl = '/auth/beatleader/login';
  const PAGE_SIZE = 5;

  $: showingStart = pagination.total > 0 ? pagination.offset + 1 : 0;
  $: showingEnd = pagination.total > 0 ? Math.min(pagination.offset + comments.length, pagination.total) : 0;

  function normalizePagination(data: GuestbookResponse, fallbackOffset: number): GuestbookPagination {
    const page = data.pagination ?? null;
    const limit = typeof page?.limit === 'number' && Number.isFinite(page.limit) && page.limit > 0 ? page.limit : PAGE_SIZE;
    const total = typeof page?.total === 'number' && page.total >= 0 ? page.total : Math.max(data.comments.length + fallbackOffset, 0);
    const offset = typeof page?.offset === 'number' && page.offset >= 0 ? page.offset : fallbackOffset;
    const hasPrev = page?.hasPrev ?? offset > 0;
    const hasNext = page?.hasNext ?? offset + data.comments.length < total;
    return { total, offset, limit, hasPrev, hasNext };
  }

  function formatTimestamp(ts: number): string {
    const now = Date.now();
    const diff = Math.max(0, now - ts);

    const minute = 60 * 1000;
    const hour = 60 * minute;
    const day = 24 * hour;
    const week = 7 * day;
    const month = 30 * day;
    const year = 365 * day;

    if (diff < minute) {
      const seconds = Math.floor(diff / 1000);
      return seconds <= 1 ? 'just now' : `${seconds} seconds ago`;
    }
    if (diff < hour) {
      const minutes = Math.floor(diff / minute);
      return minutes === 1 ? 'a minute ago' : `${minutes} minutes ago`;
    }
    if (diff < day) {
      const hours = Math.floor(diff / hour);
      return hours === 1 ? 'an hour ago' : `${hours} hours ago`;
    }
    if (diff < week) {
      const days = Math.floor(diff / day);
      return days === 1 ? 'yesterday' : `${days} days ago`;
    }
    if (diff < month) {
      const weeks = Math.floor(diff / week);
      return weeks === 1 ? 'a week ago' : `${weeks} weeks ago`;
    }
    if (diff < year) {
      const months = Math.floor(diff / month);
      return months === 1 ? 'a month ago' : `${months} months ago`;
    }
    const years = Math.floor(diff / year);
    return years === 1 ? 'a year ago' : `${years} years ago`;
  }

  async function fetchGuestbook(offset = 0): Promise<void> {
    loading = true;
    error = null;
    try {
      const params = new URLSearchParams({
        limit: String(PAGE_SIZE),
        offset: String(Math.max(0, Math.floor(offset)))
      });
      const res = await fetch(`/api/guestbook?${params.toString()}`);
      if (!res.ok) {
        const body = await res.json().catch(() => null);
        const message = body?.error ?? `Failed to load guestbook (${res.status})`;
        throw new Error(message);
      }
      const data = (await res.json()) as GuestbookResponse;
      viewer = data.viewer ?? null;
      comments = Array.isArray(data.comments) ? data.comments : [];
      pagination = normalizePagination(data, Math.max(0, offset));
    } catch (err) {
      error = err instanceof Error ? err.message : 'Failed to load guestbook.';
    } finally {
      loading = false;
    }
  }

  async function postComment(message: string): Promise<void> {
    const res = await fetch('/api/guestbook', {
      method: 'POST',
      headers: { 'content-type': 'application/json' },
      body: JSON.stringify({ message })
    });

    const payload = await res.json().catch(() => null);
    if (!res.ok) {
      const messageText = payload?.error ?? `Request failed (${res.status})`;
      throw new Error(messageText);
    }

    const data = payload as GuestbookResponse;
    viewer = data.viewer ?? viewer;
    comments = Array.isArray(data.comments) ? data.comments : comments;
    pagination = normalizePagination(data, 0);
  }

  async function deleteComment(id: string): Promise<void> {
    const params = new URLSearchParams({
      id,
      limit: String(PAGE_SIZE),
      offset: String(pagination.offset)
    });
    const res = await fetch(`/api/guestbook?${params.toString()}`, {
      method: 'DELETE'
    });

    const payload = await res.json().catch(() => null);
    if (!res.ok) {
      const messageText = payload?.error ?? `Failed to delete comment (${res.status})`;
      throw new Error(messageText);
    }

    const data = payload as GuestbookResponse;
    comments = Array.isArray(data.comments) ? data.comments : comments;
    pagination = normalizePagination(data, pagination.offset);
  }

  async function submitNewComment(): Promise<void> {
    if (!viewer) {
      newMessageError = 'You must be logged in to leave a comment.';
      return;
    }
    const trimmed = newMessage.trim();
    if (!trimmed) {
      newMessageError = 'Please enter a message before submitting.';
      return;
    }
    if (trimmed.length > 2000) {
      newMessageError = 'Message exceeds the 2000 character limit.';
      return;
    }

    submittingNew = true;
    newMessageError = null;
    try {
      await postComment(trimmed);
      newMessage = '';
    } catch (err) {
      newMessageError = err instanceof Error ? err.message : 'Failed to post comment.';
    } finally {
      submittingNew = false;
    }
  }

  async function loadNewer(): Promise<void> {
    if (loading || !pagination.hasPrev) return;
    const nextOffset = Math.max(0, pagination.offset - pagination.limit);
    await fetchGuestbook(nextOffset);
  }

  async function loadOlder(): Promise<void> {
    if (loading || !pagination.hasNext) return;
    const nextOffset = pagination.offset + pagination.limit;
    await fetchGuestbook(nextOffset);
  }

  onMount(() => {
    void fetchGuestbook(0);
  });
</script>

<section class="guestbook-section">
  <div class="guestbook-header">
    <h2 class="font-display tracking-widest">Guestbook</h2>
  </div>

  {#if viewer}
    <form class="guestbook-form" on:submit|preventDefault={submitNewComment}>
      <textarea
        id="guestbook-new-message"
        class="input"
        bind:value={newMessage}
        maxlength={2000}
        rows={4}
        placeholder="Leave your mark—drop a note in the guestbook!"
      ></textarea>
      {#if newMessageError}
        <div class="form-error">{newMessageError}</div>
      {/if}
      <div class="form-actions">
        <button class="btn-neon" type="submit" disabled={submittingNew}>
          {submittingNew ? 'Posting…' : 'Post Comment'}
        </button>
      </div>
    </form>
  {:else}
    <div class="guestbook-login">
      <p>
        <a class="underline" href={loginUrl}>Log in with BeatLeader</a> to leave a comment.
      </p>
    </div>
  {/if}

  {#if loading}
    <div class="guestbook-status">Loading comments…</div>
  {:else if error}
    <div class="guestbook-error">{error}</div>
  {:else if comments.length === 0}
    <div class="guestbook-empty">No comments yet. Be the first to say hello!</div>
  {:else}
    <ul class="guestbook-threads">
      {#each comments as comment (comment.id)}
        <li class="guestbook-thread">
          <article class="guestbook-comment">
            <div class="comment-header">
              <PlayerCard
                name={comment.author.name ?? 'Unknown'}
                country={comment.author.country}
                rank={null}
                showRank={false}
                avatar={comment.author.avatar}
                width="100%"
                avatarSize={56}
                techPp={comment.author.techPp}
                accPp={comment.author.accPp}
                passPp={comment.author.passPp}
                playerId={comment.author.beatleaderId}
                gradientId={`guestbook-${comment.id}`}
              />
            </div>
            <p class="comment-body">{comment.message}</p>
            <div class="comment-meta">
              <div class="comment-meta-left">
                {#if viewer?.beatleaderId === comment.author.beatleaderId}
                  <button
                    class="comment-delete"
                    type="button"
                    on:click={async () => {
                      try {
                        await deleteComment(comment.id);
                      } catch (err) {
                        console.error(err);
                        error = err instanceof Error ? err.message : 'Failed to delete comment.';
                      }
                    }}
                    disabled={loading}
                  >
                    Delete
                  </button>
                {/if}
              </div>
              <span class="comment-meta-right">{formatTimestamp(comment.createdAt)}</span>
            </div>
          </article>
        </li>
      {/each}
    </ul>
  {/if}

  <div class="guestbook-pagination">
    <span class="guestbook-range">
      {#if pagination.total > 0}
        Showing {showingStart}–{showingEnd} of {pagination.total}
      {:else}
        No comments yet
      {/if}
    </span>
    <div class="guestbook-pagination-buttons">
      <button
        class="guestbook-nav-button"
        type="button"
        on:click={loadNewer}
        disabled={loading || !pagination.hasPrev}
      >
        Newer
      </button>
      <button
        class="guestbook-nav-button"
        type="button"
        on:click={loadOlder}
        disabled={loading || !pagination.hasNext}
      >
        Older
      </button>
    </div>
  </div>
</section>

<style>
  .guestbook-section {
    border-radius: 1rem;
    padding: 0.625rem;
    background: linear-gradient(160deg, rgba(15, 23, 42, 0.85), rgba(4, 7, 17, 0.9));
    border: 1px solid rgba(148, 163, 184, 0.08);
    box-shadow: 0 20px 45px rgba(2, 6, 23, 0.35);
  }

  .guestbook-header {
    margin-bottom: 1.5rem;
  }

  .guestbook-form {
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
    margin-bottom: 2rem;
    width: 100%;
  }

  .input {
    width: 100%;
    border-radius: 0.75rem;
    padding: 0.85rem 1rem;
    border: 1px solid rgba(148, 163, 184, 0.15);
    background: rgba(15, 23, 42, 0.6);
    color: rgba(226, 232, 240, 0.95);
    resize: vertical;
    min-height: 120px;
  }

  .input:focus {
    outline: none;
    border-color: rgba(34, 211, 238, 0.45);
    box-shadow: 0 0 0 2px rgba(34, 211, 238, 0.15);
  }

  .form-error {
    color: rgba(248, 113, 113, 0.9);
    font-size: 0.8rem;
  }

  .form-actions {
    display: flex;
    gap: 0.75rem;
    align-items: center;
    justify-content: flex-end;
  }

  .guestbook-login {
    padding: 1rem 1.25rem;
    border-radius: 0.75rem;
    border: 1px dashed rgba(148, 163, 184, 0.25);
    background: rgba(15, 23, 42, 0.4);
    color: rgba(148, 163, 184, 0.9);
    margin-bottom: 2rem;
  }

  .guestbook-status,
  .guestbook-error,
  .guestbook-empty {
    font-size: 0.95rem;
    color: rgba(148, 163, 184, 0.85);
    padding: 1rem 0;
  }

  .guestbook-error {
    color: rgba(248, 113, 113, 0.9);
  }

  .guestbook-threads {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 1.25rem;
  }

  .guestbook-thread {
    display: flex;
    flex-direction: column;
    gap: 1rem;
  }

  .guestbook-comment {
    border: 1px solid rgba(148, 163, 184, 0.12);
    border-radius: 1rem;
    padding: 1.25rem;
    background: rgba(8, 12, 24, 0.65);
    backdrop-filter: blur(8px);
    display: grid;
    grid-template-rows: auto 1fr auto;
    gap: 0.9rem;
  }

  .comment-header {
    display: flex;
    flex-direction: column;
    gap: 0.4rem;
  }

  .comment-meta {
    font-size: 0.75rem;
    color: rgba(148, 163, 184, 0.7);
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 0.75rem;
  }

  .comment-meta-left {
    display: flex;
    align-items: center;
    gap: 0.5rem;
  }

  .comment-meta-right {
    display: inline-flex;
    align-items: center;
    justify-content: flex-end;
    min-width: 6rem;
    text-align: right;
  }

  .comment-delete {
    background: none;
    border: none;
    color: rgba(248, 113, 113, 0.85);
    font-size: 0.7rem;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    cursor: pointer;
    padding: 0.25rem 0;
  }

  .comment-delete:hover:not(:disabled) {
    color: rgba(248, 113, 113, 1);
  }

  .comment-delete:disabled {
    opacity: 0.5;
    cursor: not-allowed;
  }

  .comment-body {
    font-size: 0.95rem;
    line-height: 1.5;
    color: rgba(226, 232, 240, 0.92);
    white-space: pre-wrap;
  }

  .guestbook-pagination {
    margin-top: 1.75rem;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 0.75rem;
  }

  .guestbook-range {
    font-size: 0.8rem;
    color: rgba(148, 163, 184, 0.75);
    letter-spacing: 0.04em;
  }

  .guestbook-pagination-buttons {
    display: flex;
    gap: 0.5rem;
  }

  .guestbook-nav-button {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 0.5rem 0.95rem;
    border-radius: 9999px;
    border: 1px solid rgba(148, 163, 184, 0.25);
    background: rgba(15, 23, 42, 0.55);
    color: rgba(226, 232, 240, 0.88);
    font-size: 0.75rem;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    transition: border-color 0.2s ease, color 0.2s ease, background 0.2s ease;
  }

  .guestbook-nav-button:hover:not(:disabled) {
    border-color: rgba(34, 211, 238, 0.45);
    color: rgba(34, 211, 238, 0.95);
    background: rgba(15, 23, 42, 0.7);
  }

  .guestbook-nav-button:disabled {
    opacity: 0.45;
    cursor: not-allowed;
  }


  @media (max-width: 640px) {
    .guestbook-section {
      padding: 1.75rem;
    }

    .comment-meta {
      font-size: 0.7rem;
    }
  }
</style>

