<script lang="ts">
	import ThoughtIndicator from "./ThoughtIndicator.svelte";
	import StreamingIndicator from "./StreamingIndicator.svelte";
	import UserMessage from "./UserMessage.svelte";
	import AssistantMessage from "./AssistantMessage.svelte";
	import { Greeting } from "Enums/Greeting";
	import { Role } from "Enums/Role";
  import type { ConversationContent } from "Conversations/ConversationContent";
	import { tick } from "svelte";
	import { setIcon } from "obsidian";
	import { fade } from "svelte/transition";
	import GraphAnimation from "./GraphAnimation.svelte";

  export let messages: ConversationContent[] = [];
  export let currentThought: string | null = null;
  export let isSubmitting: boolean = false;
  export let chatContainer: HTMLDivElement;

  export function resetChatArea() {
    chatContainer.scroll({ top: 0, behavior: "instant" });
    tick().then(updateScrolledState);
  }

  export async function updateChatAreaLayout(behavior: ScrollBehavior | undefined = undefined) {
    await tick();

    requestAnimationFrame(() => {
      if (behavior) {
        scrollToLatestTurn(behavior);
      }
      tick().then(updateScrolledState);
    });
  }

  function scrollToLatestTurn(behavior: ScrollBehavior) {
    const latestTurn = chatContainer.querySelector<HTMLElement>(".message-group.latest");
    if (!latestTurn) {
      return;
    }
    const paddingTop = parseFloat(getComputedStyle(chatContainer).paddingTop) || 0;
    chatContainer.scroll({ top: latestTurn.offsetTop - paddingTop, behavior });
  }

  function scrollToBottom(behavior: ScrollBehavior) {
    chatContainer.scroll({ top: chatContainer.scrollHeight, behavior });
  }

  function updateScrolledState() {
    if (!chatContainer) {
      return;
    }
    const isScrollable = chatContainer.scrollHeight > chatContainer.clientHeight;
    scrolledToBottom = !isScrollable || Math.abs(chatContainer.scrollHeight - chatContainer.scrollTop - chatContainer.clientHeight) < 100;
  }

  let scrolledToBottom: boolean = true;

  let scrollToBottomButton: HTMLButtonElement;

  type Turn = { id: number, messages: ConversationContent[] };

  $: turns = groupTurns(messages);

  // A turn starts at each visible user message; hidden contents (tool
  // responses, planning notices, attachment stubs) stay attached to the
  // turn they belong to instead of starting phantom turns
  function groupTurns(messages: ConversationContent[]): Turn[] {
    const turns: Turn[] = [];
    for (const message of messages) {
      if ((message.role === Role.User && message.shouldDisplayContent) || turns.length === 0) {
        turns.push({ id: message.id, messages: [] });
      }
      turns[turns.length - 1].messages.push(message);
    }
    return turns;
  }

  function getGreetingByTime(): string {
    const hour = new Date().getHours();

    // Morning: 5am - 11:59am
    if (hour >= 5 && hour < 12) {
      return Greeting.Morning;
    }
    // Midday: 12pm - 4:59pm
    else if (hour >= 12 && hour < 17) {
      return Greeting.Midday;
    }
    // Evening: 5pm - 8:59pm
    else if (hour >= 17 && hour < 21) {
      return Greeting.Evening;
    }
    // Night: 9pm - 4:59am
    else {
      return Greeting.Night;
    }
  }

  $: if (scrollToBottomButton) {
    setIcon(scrollToBottomButton, "arrow-down");
  }
</script>

<div class="chat-area-wrapper">
  {#if messages.length > 0}
    <div class="top-fade"></div>
  {/if}
  <div class="chat-area" bind:this={chatContainer} on:scroll={updateScrolledState}>
    {#each turns as turn, turnIndex (turn.id)}
      {@const isLatestTurn = turnIndex === turns.length - 1}
      <div class="message-group" class:latest={isLatestTurn}>
        {#each turn.messages as message (message.id)}
          {@const content = message.getDisplayContent()}
          {#if message.shouldDisplayContent && content.trim() !== ""}
            {#if message.role === Role.User}
              <UserMessage {message} />
            {:else}
              <AssistantMessage {message} {isSubmitting} />
            {/if}
          {/if}
        {/each}

        {#if isLatestTurn}
          <ThoughtIndicator thought={currentThought}/>
          {#if isSubmitting}
            <div transition:fade={{ duration: 300 }}>
              <StreamingIndicator/>
            </div>
          {/if}
        {/if}
      </div>
    {/each}

    {#if messages.length === 0}
      <div class="conversation-empty-container">
        <div class="conversation-empty-animation">
          <GraphAnimation/>
        </div>
        <div class="conversation-empty-greeting">
          <div class="conversation-empty-greeting-backdrop"></div>
          <div class="conversation-empty-greeting-label">{getGreetingByTime()}</div>
        </div>
      </div>
    {/if}

  </div>

  {#if messages.length > 0}
    <div class="bottom-fade"></div>
  {/if}

  {#if !scrolledToBottom}
    <div class="scroll-to-bottom-container" transition:fade>
      <button
        id="scroll-to-bottom-button"
        bind:this={scrollToBottomButton}
        on:click={() => scrollToBottom("smooth")}
        aria-label="Scroll to bottom">
      </button>
    </div>
  {/if}
</div>

<style>
  .scroll-to-bottom-container {
    background-color: color-mix(in srgb, var(--background-primary) 70%, transparent);
    border-radius: var(--radius-l);
    padding: var(--size-4-2);
    position: absolute;
    bottom: var(--size-4-2);
    right: 0px;
  }

  #scroll-to-bottom-button {
    background-color: var(--interactive-accent);
  }

  .chat-area-wrapper {
    position: relative;
    height: 100%;
    width: 100%;
  }

  .top-fade {
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    height: var(--size-4-4);
    background-image: linear-gradient(to bottom, var(--background-secondary), transparent);
    z-index: 10;
    pointer-events: none;
  }

  .bottom-fade {
    position: absolute;
    bottom: 0;
    left: 0;
    right: 0;
    height: var(--size-4-4);
    background-image: linear-gradient(to top, var(--background-secondary), transparent);
    z-index: 10;
    pointer-events: none;
  }

  .chat-area {
    position: relative;
    display: flex;
    flex-direction: column;
    height: 100%;
    overflow: auto;
    padding: var(--size-4-4) var(--size-4-3) var(--size-4-3) var(--size-4-3);
    gap: var(--size-4-2);
    scroll-behavior: smooth;
  }

  .chat-area::-webkit-scrollbar {
    display: none;
  }

  .message-group {
    display: flex;
    flex-direction: column;
    flex-shrink: 0;
    gap: var(--size-4-2);
  }

  .message-group.latest {
    margin-top: var(--size-4-2);
    min-height: 100%;
  }

  .conversation-empty-container {
    display: grid;
    height: 100%;
    width: 100%;
  }

  .conversation-empty-animation {
    grid-row: 1;
    grid-column: 1;
    height: 100%;
    width: 100%;
  }

  .conversation-empty-greeting-backdrop {
    position: absolute;
    inset: -48px;
    background: radial-gradient(
      ellipse closest-side,
      var(--background-secondary) 0%,
      color-mix(in srgb, var(--background-secondary) 80%, transparent) 65%,
      transparent 100%
    );
  }

  .conversation-empty-greeting {
    grid-row: 1;
    grid-column: 1;
    position: relative;
    margin: auto;
    font-style: italic;
    font-size: var(--font-ui-medium);
    color: var(--text-muted);
    pointer-events: none;
  }

  .conversation-empty-greeting-label {
    position: relative;
    overflow: hidden;
    white-space: nowrap;
    padding: var(--size-2-2);
  }
</style>