<script lang="ts">
import { onMount } from "svelte";
import {
  App,
  applyDocumentTheme,
  applyHostFonts,
  applyHostStyleVariables,
  type McpUiHostContext,
} from "@modelcontextprotocol/ext-apps";
import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js";

function extractTime(result: CallToolResult): string {
  const { text } = result.content?.find((c) => c.type === "text")!;
  return text;
}


let app = $state<App | null>(null);
let hostContext = $state<McpUiHostContext | undefined>();
let serverTime = $state("Loading...");
let messageText = $state("This is message text.");
let logText = $state("This is log text.");
let linkUrl = $state("https://modelcontextprotocol.io/");

// Apply host styles reactively when hostContext changes
$effect(() => {
  if (hostContext?.theme) {
    applyDocumentTheme(hostContext.theme);
  }
  if (hostContext?.styles?.variables) {
    applyHostStyleVariables(hostContext.styles.variables);
  }
  if (hostContext?.styles?.css?.fonts) {
    applyHostFonts(hostContext.styles.css.fonts);
  }
});

onMount(async () => {
  const instance = new App({ name: "Get Time App", version: "1.0.0" });

  instance.ontoolinput = (params) => {
    console.info("Received tool call input:", params);
  };

  instance.ontoolresult = (result) => {
    console.info("Received tool call result:", result);
    serverTime = extractTime(result);
  };

  instance.ontoolcancelled = (params) => {
    console.info("Tool call cancelled:", params.reason);
  };

  instance.onerror = console.error;

  instance.onhostcontextchanged = (params) => {
    hostContext = { ...hostContext, ...params };
  };

  await instance.connect();
  app = instance;
  hostContext = instance.getHostContext();
});

async function handleGetTime() {
  if (!app) return;
  try {
    console.info("Calling get-time tool...");
    const result = await app.callServerTool({ name: "get-time", arguments: {} });
    console.info("get-time result:", result);
    serverTime = extractTime(result);
  } catch (e) {
    console.error(e);
    serverTime = "[ERROR]";
  }
}

async function handleSendMessage() {
  if (!app) return;
  const signal = AbortSignal.timeout(5000);
  try {
    console.info("Sending message text to Host:", messageText);
    const { isError } = await app.sendMessage(
      { role: "user", content: [{ type: "text", text: messageText }] },
      { signal },
    );
    console.info("Message", isError ? "rejected" : "accepted");
  } catch (e) {
    console.error("Message send error:", signal.aborted ? "timed out" : e);
  }
}

async function handleSendLog() {
  if (!app) return;
  console.info("Sending log text to Host:", logText);
  await app.sendLog({ level: "info", data: logText });
}

async function handleOpenLink() {
  if (!app) return;
  console.info("Sending open link request to Host:", linkUrl);
  const { isError } = await app.openLink({ url: linkUrl });
  console.info("Open link request", isError ? "rejected" : "accepted");
}
</script>

<main
  class="main"
  style:padding={hostContext?.safeAreaInsets && `${hostContext.safeAreaInsets.top}px ${hostContext.safeAreaInsets.right}px ${hostContext.safeAreaInsets.bottom}px ${hostContext.safeAreaInsets.left}px`}
>
  <p class="notice">Watch activity in the DevTools console!</p>

  <div class="action">
    <p><strong>Server Time:</strong> <code id="server-time">{serverTime}</code></p>
    <button onclick={handleGetTime}>Get Server Time</button>
  </div>

  <div class="action">
    <textarea bind:value={messageText}></textarea>
    <button onclick={handleSendMessage}>Send Message</button>
  </div>

  <div class="action">
    <input type="text" bind:value={logText}>
    <button onclick={handleSendLog}>Send Log</button>
  </div>

  <div class="action">
    <input type="url" bind:value={linkUrl}>
    <button onclick={handleOpenLink}>Open Link</button>
  </div>
</main>

<style>
.main {
  --color-primary: #2563eb;
  --color-primary-hover: #1d4ed8;
  --color-notice-bg: #eff6ff;

  width: 100%;
  max-width: 425px;
  box-sizing: border-box;

  > * {
    margin-top: 0;
    margin-bottom: 0;
  }

  > * + * {
    margin-top: 1.5rem;
  }
}

.action {
  > * {
    margin-top: 0;
    margin-bottom: 0;
    width: 100%;
  }

  > * + * {
    margin-top: 0.5rem;
  }

  textarea,
  input {
    font-family: inherit;
    font-size: inherit;
  }

  button {
    padding: 0.5rem 1rem;
    border: none;
    border-radius: 6px;
    color: white;
    font-weight: bold;
    background-color: var(--color-primary);
    cursor: pointer;

    &:hover,
    &:focus-visible {
      background-color: var(--color-primary-hover);
    }
  }
}

.notice {
  padding: 0.5rem 0.75rem;
  color: var(--color-primary);
  text-align: center;
  font-style: italic;
  background-color: var(--color-notice-bg);

  &::before {
    content: "ℹ️ ";
    font-style: normal;
  }
}
</style>
