<script lang="ts">
import type { Component } from "svelte";
import { onDestroy, onMount } from "svelte";
import { browser } from "$app/environment";
import HeadTitle from "$lib/Home/HeadTitle.svelte";
import Landing from "$lib/Landing.svelte";
import LandingHero from "$lib/LandingHero.svelte";
import Spacer from "$lib/Layout/Spacer.svelte";
import ListTitle from "$lib/List/ListTitle.svelte";
import Skeleton from "$lib/Loading/Skeleton.svelte";
import { createHeightObserver } from "$lib/Utility/html.js";
import userIdentity from "$stores/identity.js";
import locale from "$stores/locale.js";
import settings from "$stores/settings";
import stateBin, { hydrateStateBin } from "$stores/stateBin.js";
import type { PageData } from "./$types";

let { data }: { data: PageData } = $props();

type UserProp = PageData["user"];
type LastActivitySvelteComponent = Component<{
	user: UserProp;
}>;
type UpcomingAnimeListSvelteComponent = Component<{
	user: UserProp;
}>;
type IndexColumnSvelteComponent = Component<{
	user: UserProp;
	userIdentity: { id: number };
}>;
type MangaListTemplateSvelteComponent = Component<{
	user: UserProp;
	displayUnresolved: boolean;
	due: boolean;
}>;

let removeHeightObserver: (() => void) | undefined;
let LastActivityComponent: LastActivitySvelteComponent | null = $state(null);
let UpcomingAnimeListComponent: UpcomingAnimeListSvelteComponent | null =
	$state(null);
let IndexColumnComponent: IndexColumnSvelteComponent | null = $state(null);
let MangaListTemplateComponent: MangaListTemplateSvelteComponent | null =
	$state(null);
let authenticatedHomeSurfaceImport: Promise<void> | null = null;
let balancedListFlowElement: HTMLDivElement | undefined = $state();
let balancedListResizeObserver: ResizeObserver | undefined;
let balancedListMeasurementFrame = 0;
const balancedListPanelElements = new Map<string, HTMLElement>();
let balancedListPanelAssignments = $state<Record<string, "left" | "right">>({});

const resetBalancedListLayout = () => {
	balancedListPanelAssignments = {};
};

const updateBalancedListLayout = () => {
	if (!balancedListFlowElement) return;

	const balancedListFlowStyles = getComputedStyle(balancedListFlowElement);
	const balancedListRowGap = parseFloat(
		balancedListFlowStyles.getPropertyValue("row-gap"),
	);
	const balancedListColumnCount = balancedListFlowStyles
		.getPropertyValue("grid-template-columns")
		.split(" ")
		.filter(Boolean).length;

	if (balancedListColumnCount <= 1 || !Number.isFinite(balancedListRowGap)) {
		balancedListPanelAssignments = Object.fromEntries(
			Array.from(balancedListPanelElements.keys()).map((key) => [key, "left"]),
		);

		return;
	}

	const balancedListKeys = ["upcoming", "due", "manga"].filter((key) =>
		balancedListPanelElements.has(key),
	);
	const balancedListHeights = { left: 0, right: 0 };
	const nextBalancedListAssignments: Record<string, "left" | "right"> = {};

	balancedListKeys.forEach((key) => {
		const balancedListPanel = balancedListPanelElements.get(key);
		if (!balancedListPanel) return;

		const balancedListColumn =
			balancedListHeights.left <= balancedListHeights.right ? "left" : "right";
		const balancedListPanelHeight =
			balancedListPanel.getBoundingClientRect().height;

		nextBalancedListAssignments[key] = balancedListColumn;
		balancedListHeights[balancedListColumn] +=
			balancedListPanelHeight +
			(balancedListHeights[balancedListColumn] > 0 ? balancedListRowGap : 0);
	});

	balancedListPanelAssignments = nextBalancedListAssignments;
};

const queueBalancedListLayout = () => {
	if (!browser) return;

	cancelAnimationFrame(balancedListMeasurementFrame);

	balancedListMeasurementFrame = requestAnimationFrame(() => {
		updateBalancedListLayout();
	});
};

const observeBalancedListPanel = (panel: HTMLElement, key: string) => {
	balancedListPanelElements.set(key, panel);
	balancedListResizeObserver?.observe(panel);
	queueBalancedListLayout();

	return {
		destroy: () => {
			balancedListPanelElements.delete(key);
			balancedListResizeObserver?.unobserve(panel);
			queueBalancedListLayout();
		},
	};
};

const isBalancedListPanelInColumn = (key: string, column: "left" | "right") =>
	(balancedListPanelAssignments[key] ?? "left") === column;

const loadAuthenticatedHomeSurface = () => {
	if (data.user === undefined) return null;
	if (authenticatedHomeSurfaceImport) return authenticatedHomeSurfaceImport;

	authenticatedHomeSurfaceImport = Promise.all([
		import("$lib/Home/LastActivity.svelte"),
		import("$lib/List/Anime/UpcomingAnimeList.svelte"),
		import("$lib/List/Anime/DueIndexColumn.svelte"),
		import("$lib/List/Manga/MangaListTemplate.svelte"),
	]).then(
		([lastActivity, upcomingAnimeList, indexColumn, mangaListTemplate]) => {
			LastActivityComponent = lastActivity.default;
			UpcomingAnimeListComponent = upcomingAnimeList.default;
			IndexColumnComponent = indexColumn.default;
			MangaListTemplateComponent = mangaListTemplate.default;
		},
	);

	return authenticatedHomeSurfaceImport;
};

onMount(async () => {
	removeHeightObserver = createHeightObserver();
	await hydrateStateBin();
	$stateBin.upcomingAnimeListOpen ??= true;
	$stateBin.dueMangaListOpen ??= true;

	balancedListResizeObserver = new ResizeObserver(() => {
		queueBalancedListLayout();
	});

	balancedListPanelElements.forEach((panel) => {
		balancedListResizeObserver?.observe(panel);
	});

	window.addEventListener("resize", queueBalancedListLayout);
	void loadAuthenticatedHomeSurface();

	queueBalancedListLayout();
});

onDestroy(() => {
	removeHeightObserver?.();
	balancedListResizeObserver?.disconnect();
	if (browser) {
		cancelAnimationFrame(balancedListMeasurementFrame);
		window.removeEventListener("resize", queueBalancedListLayout);
	}
	resetBalancedListLayout();
});
</script>

<HeadTitle />

{#if LastActivityComponent}
  <LastActivityComponent user={data.user} />
{/if}

{#if data.user === undefined}
  <LandingHero />

  <Spacer size="lg" />

  <Landing />
{:else}
  {@const balancedListColumnCount =
    [!$settings.disableUpcomingAnime, !$settings.disableAnime, !$settings.disableManga]
      .map(Number)
      .reduce((a, b) => a + b) > 1
      ? 2
      : 1}
  <div
    bind:this={balancedListFlowElement}
    class="balanced-list-flow"
    style={`
			--balanced-list-columns: ${balancedListColumnCount}
		`}
  >
    <div class="balanced-list-column">
      {#if !$settings.disableUpcomingAnime && isBalancedListPanelInColumn("upcoming", "left")}
        <div class="balanced-list-panel" use:observeBalancedListPanel={"upcoming"}>
          <details bind:open={$stateBin.upcomingAnimeListOpen} class="list list-upcoming">
            {#if $userIdentity.id !== -2}
              {#if UpcomingAnimeListComponent}
                <UpcomingAnimeListComponent user={data.user} />
              {:else}
                <ListTitle title={$locale().lists.upcoming.episodes} />

                <Skeleton card={false} count={5} height="0.9rem" list />
              {/if}
            {:else}
              <ListTitle title={$locale().lists.upcoming.episodes} />

              <Skeleton card={false} count={5} height="0.9rem" list />
            {/if}
          </details>
        </div>
      {/if}

      {#if !$settings.disableAnime && isBalancedListPanelInColumn("due", "left")}
        <div class="balanced-list-panel" use:observeBalancedListPanel={"due"}>
          {#if IndexColumnComponent}
            <IndexColumnComponent user={data.user} userIdentity={$userIdentity} />
          {:else}
            <details bind:open={$stateBin.dueAnimeListOpen} class="list list-due">
              <ListTitle title={$locale().lists.due.episodes} />

              <Skeleton card={false} count={5} height="0.9rem" list />
            </details>
          {/if}
        </div>
      {/if}

      {#if !$settings.disableManga && isBalancedListPanelInColumn("manga", "left")}
        <div class="balanced-list-panel" use:observeBalancedListPanel={"manga"}>
          <details bind:open={$stateBin.dueMangaListOpen} class="list list-manga">
            {#if $userIdentity.id !== -2}
              {#if MangaListTemplateComponent}
                <MangaListTemplateComponent
                  user={data.user}
                  displayUnresolved={$settings.displayUnresolved}
                  due={true}
                />
              {:else}
                <ListTitle title={$locale().lists.due.mangaAndLightNovels} />

                <Skeleton card={false} count={5} height="0.9rem" list />
              {/if}
            {:else}
              <ListTitle title={$locale().lists.due.mangaAndLightNovels} />

              <Skeleton card={false} count={5} height="0.9rem" list />
            {/if}
          </details>
        </div>
      {/if}
    </div>

    {#if balancedListColumnCount > 1}
      <div class="balanced-list-column">
        {#if !$settings.disableUpcomingAnime && isBalancedListPanelInColumn("upcoming", "right")}
          <div class="balanced-list-panel" use:observeBalancedListPanel={"upcoming"}>
            <details bind:open={$stateBin.upcomingAnimeListOpen} class="list list-upcoming">
              {#if $userIdentity.id !== -2}
                {#if UpcomingAnimeListComponent}
                  <UpcomingAnimeListComponent user={data.user} />
                {:else}
                  <ListTitle title={$locale().lists.upcoming.episodes} />

                  <Skeleton card={false} count={5} height="0.9rem" list />
                {/if}
              {:else}
                <ListTitle title={$locale().lists.upcoming.episodes} />

                <Skeleton card={false} count={5} height="0.9rem" list />
              {/if}
            </details>
          </div>
        {/if}

        {#if !$settings.disableAnime && isBalancedListPanelInColumn("due", "right")}
          <div class="balanced-list-panel" use:observeBalancedListPanel={"due"}>
            {#if IndexColumnComponent}
              <IndexColumnComponent user={data.user} userIdentity={$userIdentity} />
            {:else}
              <details bind:open={$stateBin.dueAnimeListOpen} class="list list-due">
                <ListTitle title={$locale().lists.due.episodes} />

                <Skeleton card={false} count={5} height="0.9rem" list />
              </details>
            {/if}
          </div>
        {/if}

        {#if !$settings.disableManga && isBalancedListPanelInColumn("manga", "right")}
          <div class="balanced-list-panel" use:observeBalancedListPanel={"manga"}>
            <details bind:open={$stateBin.dueMangaListOpen} class="list list-manga">
              {#if $userIdentity.id !== -2}
                {#if MangaListTemplateComponent}
                  <MangaListTemplateComponent
                    user={data.user}
                    displayUnresolved={$settings.displayUnresolved}
                    due={true}
                  />
                {:else}
                  <ListTitle title={$locale().lists.due.mangaAndLightNovels} />

                  <Skeleton card={false} count={5} height="0.9rem" list />
                {/if}
              {:else}
                <ListTitle title={$locale().lists.due.mangaAndLightNovels} />

                <Skeleton card={false} count={5} height="0.9rem" list />
              {/if}
            </details>
          </div>
        {/if}
      </div>
    {/if}

    {#if $settings.disableUpcomingAnime && $settings.disableAnime && $settings.disableManga}
      <video src="https://video.twimg.com/tweet_video/Do_eDPnX0AAKV9f.mp4" autoplay loop>
        <track kind="captions" />
      </video>
    {/if}
  </div>
{/if}

<style lang="scss">
  @use "breakpoints" as *;

  .balanced-list-flow {
    display: grid;
    grid-template-columns: repeat(var(--balanced-list-columns), minmax(0, 1fr));
    gap: 1rem;
    align-items: start;
  }

  .balanced-list-column {
    display: grid;
    gap: 1rem;
    align-content: start;
    min-width: 0;
  }

  .balanced-list-panel {
    min-width: 0;
  }

  .list {
    overflow-y: auto;
    margin: 0;
  }

  @media (max-width: $bp-md) {
    .balanced-list-flow {
      grid-template-columns: 1fr;
    }
  }
</style>
