<script lang='ts'>
  import { onMount } from 'svelte';

  type Item = {
    id: number;
    email: string;
    first_name: string;
    last_name: string;
    created: Date;
    updated: Date;
  }
  const _tableColumnCount = 6;

  let currentState: 'finished' | 'loading' | 'error';
  let errorMessages: string[] = [];

  let currentItems: Item[] = [];
  let totalItemCount: number | null;

  const itemsPerPageOptions = [10, 25, 50, 100];
  let currentItemsPerPage: number;

  let currentPage: number;
  let lastPage: number;
  $: if (totalItemCount) {
    lastPage = Math.ceil(totalItemCount / currentItemsPerPage) - 1;
  } else {
    lastPage = 0;
  }

  $: currentItemsOffset = currentPage * currentItemsPerPage;

  $: gotoPrevPageDisabled = currentState !== 'finished' || currentPage === 0 || lastPage === 0;
  async function handleGotoPrevPage() {
    if (gotoPrevPageDisabled) return;

    currentPage -= 1;
    await updateTable();
  }

  $: gotoNextPageDisabled = currentState !== 'finished' || currentPage === lastPage || lastPage === 0;
  async function handleGotoNextPage() {
    if (gotoNextPageDisabled) return;

    currentPage += 1;
    await updateTable();
  }

  $: gotoFirstPageDisabled = currentState !== 'finished' || currentPage === 0 || lastPage === 0;
  async function handleGotoFirstPage() {
    if (gotoFirstPageDisabled) return;

    currentPage = 0;
    await updateTable();
  }

  $: gotoLastPageDisabled = currentState !== 'finished' || currentPage === lastPage || lastPage === 0;
  async function handleGotoLastPage() {
    if (gotoLastPageDisabled) return;

    currentPage = lastPage;
    await updateTable();
  }

  interface ApiError {
    // FIXME error message parsing doesn't work right now
    detail: { msg: string; loc?: string[]; type?: string }[]
  }

  async function getTotalItemCount(): Promise<number> {
    interface ApiResponse {
      total: number
    }

    const response = await fetch(`/api/users/total/`);
    const json = await response.json();

    if (!response.ok) {
      // Capture error messages from response body
      const apiError = json as ApiError;
      for (let detail of apiError.detail) {
        errorMessages.push(detail.msg);
      }
      throw new Error(`API request failed with status ${response.status}: ${response.statusText}`);
    }

    const apiResponse = json as ApiResponse;
    return apiResponse.total;
  }

  async function getItems(offset: number = currentItemsOffset, count: number = currentItemsPerPage): Promise<Item[]> {
    const urlParameters = new URLSearchParams({
      skip: `${offset}`,
      limit: `${count}`,
    });

    const response = await fetch(`/api/users/?${urlParameters}`);
    const json = await response.json();

    if (!response.ok) {
      // Capture error messages from response body
      const apiError = json as ApiError;
      for (let detail of apiError.detail) {
        errorMessages.push(detail.msg);
      }
      throw new Error(`API request failed with status ${response.status}: ${response.statusText}`);
    }

    const apiResponse = json as Item[];
    return apiResponse;
  }

  async function updateTable() {
    try {
      currentState = 'loading';
      totalItemCount = await getTotalItemCount();
      currentItems = await getItems();
      currentState = 'finished';
    } catch (error) {
      console.log(error);
      currentState = 'error';
    }
  }

  onMount(async () => {
    // Initialize vars
    currentItems = [];
    totalItemCount = null;
    currentItemsPerPage  = itemsPerPageOptions[0];
    currentPage = 0;
    currentState = 'finished';

    await updateTable();
  });
</script>

<table>
  <caption>List of users</caption>
  <thead>
    <tr>
      <th>ID</th>
      <th>Email</th>
      <th>First Name</th>
      <th>Last Name</th>
      <th>Created</th>
      <th>Updated</th>
    </tr>
  </thead>
  <tbody>
    {#if currentState === 'finished'}
      {#each currentItems as user}
        <tr>
          <td>{user.id}</td>
          <td>{user.email}</td>
          <td>{user.first_name}</td>
          <td>{user.last_name}</td>
          <td>{new Date(user.created).toLocaleDateString()}</td>
          <td>{new Date(user.updated).toLocaleDateString()}</td>
        </tr>
      {/each}
    {:else if currentState === 'loading'}
      <tr>
        <td colspan={_tableColumnCount}>Loading...</td>
      </tr>
    {:else if currentState === 'error'}
      <tr>
        {#each errorMessages as message}
          <td colspan={_tableColumnCount} class="text-red-500">{message}</td>
        {/each}
      </tr>
    {/if}
  </tbody>
</table>
<button on:click={handleGotoFirstPage} disabled={gotoFirstPageDisabled}>
  <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-5 h-5">
    <path fill-rule="evenodd" d="M15.79 14.77a.75.75 0 01-1.06.02l-4.5-4.25a.75.75 0 010-1.08l4.5-4.25a.75.75 0 111.04 1.08L11.832 10l3.938 3.71a.75.75 0 01.02 1.06zm-6 0a.75.75 0 01-1.06.02l-4.5-4.25a.75.75 0 010-1.08l4.5-4.25a.75.75 0 111.04 1.08L5.832 10l3.938 3.71a.75.75 0 01.02 1.06z" clip-rule="evenodd" />
  </svg>
</button>
<button on:click={handleGotoPrevPage} disabled={gotoPrevPageDisabled}>
  <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-5 h-5">
    <path fill-rule="evenodd" d="M12.79 5.23a.75.75 0 01-.02 1.06L8.832 10l3.938 3.71a.75.75 0 11-1.04 1.08l-4.5-4.25a.75.75 0 010-1.08l4.5-4.25a.75.75 0 011.06.02z" clip-rule="evenodd" />
  </svg>
</button>
<button on:click={handleGotoNextPage} disabled={gotoNextPageDisabled}>
  <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-5 h-5">
    <path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" />
  </svg>
</button>
<button on:click={handleGotoLastPage} disabled={gotoLastPageDisabled}>
  <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-5 h-5">
    <path fill-rule="evenodd" d="M10.21 14.77a.75.75 0 01.02-1.06L14.168 10 10.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" />
    <path fill-rule="evenodd" d="M4.21 14.77a.75.75 0 01.02-1.06L8.168 10 4.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" />
  </svg>
</button>
