<script lang="ts">
  import {
    GroupID,
    UserID,
    ServerID,
    type GroupId,
    type ServerId,
    type UserId,
    type OverviewUser,
    type OverviewGroup,
    type OverviewServer,
    type ReturnMessage,
    type Channel,
    type ChannelId,
    ChannelID,
    statuses
  } from '$lib';
  import type { PageServerData } from './$types';
  import MainSidebar from '$lib/components/main-sidebar.svelte';
  import * as Sidebar from '$lib/components/ui/sidebar/index.js';

  import { onMount } from 'svelte';
  import type { ActionData } from './$types';
  import { formatTimestamp } from '$lib/utils';
  import Input from '$lib/components/ui/input/input.svelte';
  import { Button } from '$lib/components/ui/button';

  import ServerDashboard from './ServerDashboard.svelte';
  import SendHorizontal from '@lucide/svelte/icons/send-horizontal';
  import PersonStanding from '@lucide/svelte/icons/person-standing';
  import MemberSidebar from '$lib/components/member-sidebar.svelte';
  import { invalidateAll } from '$app/navigation';
  import { toast } from 'svelte-sonner';
  import { fill_overview_data, overview_data } from '$lib/state.svelte';

  let { form, data }: { form: ActionData; data: PageServerData } = $props();
  let currentPageID: (UserId | GroupId | ServerId) | null = $state(null);
  let currentSubPageID: ChannelId | null = $state(null);
  let currentPage: OverviewUser | OverviewGroup | OverviewServer | undefined = $state();
  let currentSubPage: Channel | null = $state(null);

  let sse: EventSource | undefined;
  let messagesElement: HTMLDivElement | undefined = $state();
  let isMembersTabOpen = $state(true);
  let members: OverviewUser[] = $state([]);

  let messages: ReturnMessage[] = $state([]);
  let inputValue = $state();

  let sessionId: string | undefined = $state();
  let previousSubscription: UserId | GroupId | ServerId | null = $state(null);

  $effect(() => {
    if (currentPageID) {
      if (UserID.is(currentPageID)) {
        currentPage = overview_data.friends.find((friend) => friend.id === currentPageID);
      } else if (GroupID.is(currentPageID)) {
        currentPage = overview_data.groups.find((group) => group.id === currentPageID);
      } else if (ServerID.is(currentPageID)) {
        currentPage = overview_data.servers.find((server) => server.id === currentPageID);
      }

      if (currentSubPageID) {
        currentSubPage = (currentPage as OverviewServer).channels.find(
          (z) => z.id == currentSubPageID
        ) as Channel | null;
      } else {
        currentSubPage = null;
      }
    } else {
      currentPage = undefined;
    }
  });

  $effect(() => {
    if (!currentPageID || !currentPage) return;
    if (ServerID.is(currentPageID) || GroupID.is(currentPageID)) {
      async function fetchMembers() {
        const req = await fetch(`/api/members/${currentPageID}`);
        const data = await req.json();
        members = data.members;
      }
      fetchMembers();
    } else {
      isMembersTabOpen = false;
    }
  });

  $effect(() => {
    if (!currentPageID || !currentPage) return;
    if (ServerID.is(currentPageID) && !ChannelID.is(currentSubPageID)) return;

    async function getMessages() {
      let path = '';
      let subscribe: string | null | undefined = '';

      if (ServerID.is(currentPageID) && ChannelID.is(currentSubPageID)) {
        path = '/api/messages/' + currentPageID + '/' + currentSubPageID;
        subscribe = currentSubPageID;
      } else {
        subscribe = currentPage && 'dmId' in currentPage ? currentPage.dmId : currentPageID;
        path = '/api/messages/' + subscribe;
      }

      if (!subscribe) return;
      const req = await fetch(path);
      const data = await req.json();

      messages = data.messages;

      if (previousSubscription && previousSubscription != subscribe) {
        await fetch('/api/updates/' + sessionId, {
          body: JSON.stringify({ subscribeTo: previousSubscription }),
          method: 'DELETE'
        });
      }

      if (previousSubscription != subscribe) {
        await fetch('/api/updates/' + sessionId, {
          body: JSON.stringify({ subscribeTo: subscribe }),
          method: 'POST'
        });
      }

      previousSubscription = subscribe;
    }

    getMessages();
  });

  onMount(() => {
    async function run() {
      if (form) {
        if (form.success) {
          toast.success('Action succeeded.');
        }

        if (form.error) {
          toast.error('Action had a error: ' + form.error);
        }

        await invalidateAll();
      }
      await fill_overview_data(data);
    }
    run();
  });

  onMount(() => {
    if (!sse) {
      sse = new EventSource('/api/updates');
    }

    sse.addEventListener('message', async (e) => {
      const json = JSON.parse(e.data) as
        | { type: 'connected'; sessionId: string }
        | { type: 'message'; message: ReturnMessage }
        | { type: 'status'; id: string; status: 1 | 2 | 3; statusMessage: string }
        | { type: 'friends'; status: string }
        | { type: 'group'; status: string }
        | { type: 'username'; status: string }
        | { type: 'server'; status: string };
      if (json.type == 'friends') {
        toast('Invalidation from friends updates, recieved ' + json.status);
        await invalidateAll();
        await fill_overview_data(data);
      }
      if (json.type == 'server') {
        toast('Invalidation from servers updates, recieved ' + json.status);
        await invalidateAll();
        await fill_overview_data(data);
      }
      if (json.type == 'group') {
        toast('Invalidation from group updates, recieved ' + json.status);
        if (json.status == 'removed-from-group' && GroupID.is(currentPageID)) {
          currentPageID = null;
          currentPage = undefined;
        }

        await invalidateAll();
        await fill_overview_data(data);
      }
      if (json.type == 'username') {
        toast('Invalidation from username updates, recieved ' + json.status);
        await invalidateAll();
        await fill_overview_data(data);
      }
      if (json.type == 'connected') {
        console.log('SSE connected. We are sessionID ' + json.sessionId);
        sessionId = json.sessionId;
      }

      if (json.type == 'status') {
        statuses.set(json.id, {
          status: json.status,
          statusMessage: json.statusMessage
        });
      }

      if (json.type == 'message') {
        if (!messagesElement) return;

        if (
          messagesElement.scrollTop ===
          messagesElement.scrollHeight - messagesElement.offsetHeight
        ) {
          setTimeout(() => {
            if (!messagesElement) return;
            messagesElement.scrollTop = messagesElement.scrollHeight;
          }, 66.75); // FPS
        }
        messages.push(json.message);
      }
    });
  });
</script>

<Sidebar.Provider>
  <MainSidebar
    psd={data}
    bind:subPage={currentSubPageID}
    bind:currentPage={currentPageID}
    user={data.user}
  />
  <Sidebar.Inset class="h-svh">
    <header class="flex h-16 shrink-0 items-center gap-2 border-b px-4">
      <Sidebar.Trigger class="-ms-1" />
      {#if currentPageID && currentPage}
        {#if ServerID.is(currentPageID)}
          {@const server = currentPage as OverviewServer}
          {@const channel = currentSubPage as OverviewServer}

          <img src={server!.image} alt={server!.name} class="size-6 rounded-full" />

          {#if currentSubPage}
            <h1>{server!.name} - {channel.name}</h1>
          {:else}
            <h1>{server!.name} - Server Info</h1>
          {/if}
        {:else if UserID.is(currentPageID)}
          {@const friend = currentPage as OverviewUser}

          <img src={friend.image} alt={friend!.username} class="size-6 rounded-full" />

          <h1>
            {friend!.username}
          </h1>
        {:else if GroupID.is(currentPageID)}
          {@const group = currentPage as OverviewGroup}

          <h1>{group!.name} ({group.members} member{group.members > 1 ? 's' : ''})</h1>
        {/if}
        {#if ServerID.is(currentPageID) || GroupID.is(currentPageID)}
          <Button
            variant="outline"
            onclick={() => {
              isMembersTabOpen = !isMembersTabOpen;
            }}
          >
            <PersonStanding></PersonStanding>
            <PersonStanding></PersonStanding>
            <PersonStanding></PersonStanding>
            <PersonStanding></PersonStanding>
          </Button>
        {/if}
      {/if}
    </header>

    {#if currentPageID && currentPage && ServerID.is(currentPageID) && !currentSubPageID}
      <ServerDashboard
        bind:currentPage={currentPageID}
        psd={data}
        server={currentPage as OverviewServer}
      ></ServerDashboard>
    {/if}
    {#if currentPageID && currentPage && ((ServerID.is(currentPageID) && ChannelID.is(currentSubPageID)) || UserID.is(currentPageID) || GroupID.is(currentPageID))}
      <div class="h-min shrink overflow-y-scroll" bind:this={messagesElement}>
        {#each messages as message, i (message.id)}
          {#if i === 0 || messages[i - 1].authorId !== message.authorId}
            <div class="flex gap-2 px-4 py-2">
              <img
                src={'https://api.dicebear.com/9.x/glass/svg?seed=' + message.author.name}
                alt={message.author.name}
                class="h-6 w-6 rounded-full"
              />

              <div class="flex flex-col">
                <div class="flex items-baseline gap-2">
                  <span class="font-semibold">{message.author.name}</span>
                  <span class="text-xs text-gray-400">
                    {formatTimestamp(message.timestamp)}
                  </span>
                </div>

                <div class="whitespace-pre-wrap">{message.content}</div>
              </div>
            </div>
          {:else}
            <div class="ml-8 flex gap-8 px-4 py-1">
              <div class="whitespace-pre-wrap">{message.content}</div>
            </div>
          {/if}
        {/each}
      </div>
      <div class="flex shrink-0 gap-2 border-t p-2">
        <Input
          bind:value={inputValue}
          onkeydown={async (e) => {
            if (e.key == 'Enter') {
              let path = '';

              if (ServerID.is(currentPageID) && ChannelID.is(currentSubPageID)) {
                path = '/api/messages/' + currentPageID + '/' + currentSubPageID;
              } else {
                path =
                  '/api/messages/' +
                  (currentPage && 'dmId' in currentPage ? currentPage.dmId : currentPageID);
              }
              let message = inputValue;

              inputValue = '';

              const req = await fetch(path, {
                method: 'POST',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify({ content: message })
              });

              if (!req.ok) {
                console.error('Failed to send message');
              }
            }
          }}
          placeholder="input box for messages (ignore how ugly it is right now please)"
          class="flex-1"
        />
        <Button
          onclick={async () => {
            let message = inputValue;
            const targetId =
              currentPage && 'dmId' in currentPage ? currentPage.dmId : currentPageID;
            inputValue = '';

            const req = await fetch('/api/messages/' + targetId, {
              method: 'POST',
              headers: { 'Content-Type': 'application/json' },
              body: JSON.stringify({ content: message })
            });

            if (!req.ok) {
              console.error('Failed to send message');
            }
          }}
        >
          <SendHorizontal></SendHorizontal>
        </Button>
      </div>
    {/if}
  </Sidebar.Inset>
  <Sidebar.Provider class="w-0">
    <Sidebar.Provider class="w-0">
      {#if currentPage && currentPageID && overview_data && members && !UserID.is(currentPageID)}
        <MemberSidebar
          bind:open={isMembersTabOpen}
          psd={data}
          user={data.user}
          {members}
          currentEntity={currentPage as OverviewGroup | OverviewServer}
          currentEntityId={currentPageID}
        />
      {/if}
    </Sidebar.Provider>
  </Sidebar.Provider>
</Sidebar.Provider>
