<script lang="ts">
  import { invoke } from "@tauri-apps/api/core";
  import { open } from "@tauri-apps/plugin-dialog";
  import { readImage } from "@tauri-apps/plugin-clipboard-manager";
  import { get } from "svelte/store";
  import { claudeStore, isClaudeProcessing } from "$lib/stores/claude";
  import { characterState } from "$lib/stores/character";
  import { handleNewUserMessage } from "$lib/notifications/rules";
  import { setSkipNextGreeting, updateDiscordRpc } from "$lib/tauri";
  import { clipboardStore } from "$lib/stores/clipboard";
  import {
    setShouldRestoreHistory,
    setSavedHistory,
    getShouldRestoreHistory,
    getSavedHistory,
    clearHistoryRestore,
  } from "$lib/stores/historyRestore";
  import MessageModeSelector from "$lib/components/MessageModeSelector.svelte";
  import SlashCommandMenu from "$lib/components/SlashCommandMenu.svelte";
  import SystemClock from "$lib/components/SystemClock.svelte";
  import CliVersion from "$lib/components/CliVersion.svelte";
  import { getCurrentMode } from "$lib/stores/messageMode";
  import { formatMessageWithMode } from "$lib/types/messageMode";
  import {
    parseSlashCommand,
    getMatchingCommands,
    isSlashCommand,
    type SlashCommand,
  } from "$lib/commands/slashCommands";
  import { configStore, isStreamerMode } from "$lib/stores/config";
  import { conversationsStore } from "$lib/stores/conversations";
  import { stats, estimateMessageCost, formatTokenCount } from "$lib/stores/stats";
  import AttachmentPreview from "$lib/components/AttachmentPreview.svelte";
  import SnippetLibraryPanel from "$lib/components/SnippetLibraryPanel.svelte";
  import QuickActionsPanel from "$lib/components/QuickActionsPanel.svelte";
  import ClipboardHistoryPanel from "$lib/components/ClipboardHistoryPanel.svelte";
  import TextInputContextMenu from "$lib/components/TextInputContextMenu.svelte";
  import type { Attachment } from "$lib/types/messages";

  const INPUT_HISTORY_KEY = "hikari-input-history";
  const MAX_HISTORY_SIZE = 100;

  let inputValue = $state("");
  let isSubmitting = $state(false);
  let isConnected = $state(false);
  let isProcessing = $state(false);
  let showCommandMenu = $state(false);
  let matchingCommands = $state<SlashCommand[]>([]);
  let selectedCommandIndex = $state(0);
  let attachments = $state<Attachment[]>([]);
  let isDragging = $state(false);
  let showSnippetLibrary = $state(false);
  let showQuickActions = $state(false);
  let showClipboardHistory = $state(false);
  let streamerModeActive = $state(false);

  // Cost estimation for pre-submission display
  let costEstimate = $derived(
    inputValue.trim()
      ? estimateMessageCost(inputValue, $stats.context_tokens_used, $stats.model)
      : null
  );

  // Context menu state
  let textareaElement: HTMLTextAreaElement | undefined = $state();
  let contextMenuShow = $state(false);
  let contextMenuX = $state(0);
  let contextMenuY = $state(0);

  function handleContextMenu(event: MouseEvent) {
    event.preventDefault();
    contextMenuShow = true;
    contextMenuX = event.clientX;
    contextMenuY = event.clientY;
  }

  function closeContextMenu() {
    contextMenuShow = false;
  }

  isStreamerMode.subscribe((value) => {
    streamerModeActive = value;
  });

  // Input history state
  let inputHistory = $state<string[]>([]);
  let historyIndex = $state(-1);
  let tempInput = $state("");
  let userHasTyped = $state(false); // Track if user manually typed (vs history navigation)

  // Textarea resize state
  let textareaHeight = $state(48);
  const MIN_HEIGHT = 48;
  const MAX_HEIGHT = 200;
  let isResizing = $state(false);
  let startY = 0;
  let startHeight = 0;

  function handleResizeStart(event: MouseEvent) {
    isResizing = true;
    startY = event.clientY;
    startHeight = textareaHeight;
    document.addEventListener("mousemove", handleResizeMove);
    document.addEventListener("mouseup", handleResizeEnd);
    event.preventDefault();
  }

  function handleResizeMove(event: MouseEvent) {
    if (!isResizing) return;
    // Dragging up (negative deltaY) should increase height
    const deltaY = startY - event.clientY;
    const newHeight = Math.min(MAX_HEIGHT, Math.max(MIN_HEIGHT, startHeight + deltaY));
    textareaHeight = newHeight;
  }

  function handleResizeEnd() {
    isResizing = false;
    document.removeEventListener("mousemove", handleResizeMove);
    document.removeEventListener("mouseup", handleResizeEnd);
  }

  // Load history from localStorage on init
  function loadHistory(): string[] {
    try {
      const stored = localStorage.getItem(INPUT_HISTORY_KEY);
      return stored ? JSON.parse(stored) : [];
    } catch {
      return [];
    }
  }

  function saveHistory(history: string[]) {
    try {
      localStorage.setItem(INPUT_HISTORY_KEY, JSON.stringify(history));
    } catch {
      // Ignore storage errors
    }
  }

  function addToHistory(input: string) {
    const trimmed = input.trim();
    if (!trimmed) return;

    // Don't add duplicates of the most recent entry
    if (inputHistory.length > 0 && inputHistory[0] === trimmed) return;

    // Add to front of history
    inputHistory = [trimmed, ...inputHistory.slice(0, MAX_HISTORY_SIZE - 1)];
    saveHistory(inputHistory);
  }

  // Initialize history on mount
  inputHistory = loadHistory();

  claudeStore.connectionStatus.subscribe((status) => {
    isConnected = status === "connected";
  });

  isClaudeProcessing.subscribe((processing) => {
    isProcessing = processing;
  });

  claudeStore.attachments.subscribe((storedAttachments) => {
    attachments = storedAttachments;
  });

  function handleInputChange() {
    // If input is empty, allow history navigation again
    // Otherwise, mark that user has manually typed
    if (inputValue === "") {
      userHasTyped = false;
    } else {
      userHasTyped = true;
    }
    // Reset history navigation when user types
    historyIndex = -1;
    tempInput = "";

    if (isSlashCommand(inputValue)) {
      matchingCommands = getMatchingCommands(inputValue);
      showCommandMenu = matchingCommands.length > 0;
      selectedCommandIndex = 0;
    } else {
      showCommandMenu = false;
      matchingCommands = [];
    }
  }

  function selectCommand(command: SlashCommand) {
    inputValue = `/${command.name} `;
    showCommandMenu = false;
    matchingCommands = [];
  }

  async function executeSlashCommand(): Promise<boolean> {
    const { command, args } = parseSlashCommand(inputValue);
    if (command) {
      inputValue = "";
      showCommandMenu = false;
      matchingCommands = [];
      await command.execute(args);
      return true;
    }
    return false;
  }

  async function handleSubmit(event: Event) {
    event.preventDefault();

    const message = inputValue.trim();
    const hasAttachments = attachments.length > 0;

    // Need either a message or attachments to submit
    if ((!message && !hasAttachments) || isSubmitting) return;

    // Check for slash commands first (these work even when disconnected)
    if (message && isSlashCommand(message)) {
      // Add slash commands to history too
      addToHistory(message);
      historyIndex = -1;
      tempInput = "";
      userHasTyped = false;

      const wasCommand = await executeSlashCommand();
      if (wasCommand) return;
      // If it started with / but wasn't a valid command, show error
      claudeStore.addLine(
        "error",
        `Unknown command: ${message.split(" ")[0]}. Type /help for available commands.`
      );
      inputValue = "";
      return;
    }

    // Regular messages require connection
    if (!isConnected) return;

    // Add to history before clearing (only if there's text)
    if (message) {
      addToHistory(message);
    }
    historyIndex = -1;
    tempInput = "";
    userHasTyped = false;

    isSubmitting = true;
    inputValue = "";

    // Capture attachments before clearing
    const currentAttachments = [...attachments];

    // Apply mode prefix if needed (only if there's a message)
    const currentMode = getCurrentMode();
    const formattedMessage = message ? formatMessageWithMode(message, currentMode) : "";

    // Build message with attachments
    let messageWithAttachments = formattedMessage;
    if (currentAttachments.length > 0) {
      const attachmentPaths = currentAttachments.map((a) => a.path).join("\n");
      const attachmentPrefix = formattedMessage ? `${formattedMessage}\n\n` : "";
      messageWithAttachments = `${attachmentPrefix}[Attached files - please read these files to see their contents:]\n${attachmentPaths}`;

      // Log attached files to the output
      for (const attachment of currentAttachments) {
        claudeStore.addLine("system", `📎 Attached: ${attachment.filename} (${attachment.path})`);
      }
    }

    // Clear attachments after capturing them
    claudeStore.clearAttachments();

    // Check if we need to restore conversation history
    let messageToSend = messageWithAttachments;
    if (getShouldRestoreHistory()) {
      const savedHistory = getSavedHistory();

      if (savedHistory) {
        // Prepend the conversation history with a context message
        messageToSend = `[Previous conversation context:]
${savedHistory}

[Continuing conversation after reconnection:]
User: ${formattedMessage}`;

        // Clear the restoration flags
        clearHistoryRestore();
      }
    }

    // Reset notification state for new user message
    handleNewUserMessage();

    claudeStore.addLine("user", formattedMessage);
    characterState.setState("thinking");

    try {
      const conversationId = get(claudeStore.activeConversationId);
      if (!conversationId) {
        throw new Error("No active conversation");
      }
      await invoke("send_prompt", {
        conversationId,
        message: messageToSend,
      });
    } catch (error) {
      console.error("Failed to send prompt:", error);
      claudeStore.addLine("error", `Failed to send: ${error}`);
      characterState.setTemporaryState("error", 3000);
    } finally {
      isSubmitting = false;
    }
  }

  async function handleInterrupt() {
    // Save the conversation history FIRST before anything else
    const history = claudeStore.getConversationHistory();

    if (history) {
      setSavedHistory(history);
      setShouldRestoreHistory(true);
    }

    try {
      const conversationId = get(claudeStore.activeConversationId);
      if (!conversationId) {
        throw new Error("No active conversation");
      }
      await invoke("interrupt_claude", { conversationId });
      claudeStore.addLine("system", "Process interrupted - reconnecting...");
      characterState.setState("idle");

      // Show connecting status while we reconnect
      claudeStore.setConnectionStatus("connecting");

      // Auto-reconnect after a brief delay
      setTimeout(async () => {
        try {
          const conversationId = get(claudeStore.activeConversationId);
          if (!conversationId) {
            throw new Error("No active conversation");
          }

          // Get current working directory and granted tools before reconnecting
          const workingDir = await invoke<string>("get_working_directory", { conversationId });
          const activeConversation = get(conversationsStore.activeConversation);
          const grantedTools = activeConversation
            ? Array.from(activeConversation.grantedTools)
            : [];
          const config = configStore.getConfig();
          const allAllowedTools = [...new Set([...grantedTools, ...config.auto_granted_tools])];

          // Set the flag to skip greeting on next connection
          setSkipNextGreeting(true);

          // Reconnect to Claude with preserved permissions
          await invoke("start_claude", {
            conversationId,
            options: {
              working_dir: workingDir,
              model: config.model || null,
              api_key: config.api_key || null,
              custom_instructions: config.custom_instructions || null,
              mcp_servers_json: config.mcp_servers_json || null,
              allowed_tools: allAllowedTools,
            },
          });

          // Update Discord RPC when reconnecting
          if (activeConversation) {
            await updateDiscordRpc(
              activeConversation.name,
              config.model || "claude",
              activeConversation.startedAt
            );
          }
        } catch (reconnectError) {
          console.error("Failed to auto-reconnect:", reconnectError);
          claudeStore.addLine("error", `Failed to reconnect: ${reconnectError}`);
          claudeStore.addLine("system", "Please manually reconnect to continue");
        }
      }, 500); // Brief delay to ensure process is fully terminated
    } catch (error) {
      console.error("Failed to interrupt:", error);
      claudeStore.addLine("error", `Failed to interrupt: ${error}`);
    }
  }

  function handleRemoveAttachment(id: string) {
    claudeStore.removeAttachment(id);
  }

  function getFileTypeFromExtension(extension: string): "image" | "document" | "other" {
    const imageExtensions = ["png", "jpg", "jpeg", "gif", "webp", "svg", "bmp"];
    const documentExtensions = ["pdf", "txt", "md", "doc", "docx", "csv", "json", "xml"];

    if (imageExtensions.includes(extension)) {
      return "image";
    } else if (documentExtensions.includes(extension)) {
      return "document";
    }
    return "other";
  }

  function handleDragEnter(event: DragEvent) {
    event.preventDefault();
    event.stopPropagation();
    if (event.dataTransfer?.types.includes("Files")) {
      isDragging = true;
    }
  }

  function handleDragOver(event: DragEvent) {
    event.preventDefault();
    event.stopPropagation();
    if (event.dataTransfer) {
      event.dataTransfer.dropEffect = "copy";
    }
  }

  function handleDragLeave(event: DragEvent) {
    event.preventDefault();
    event.stopPropagation();
    // Only set isDragging to false if we're leaving the form element entirely
    const relatedTarget = event.relatedTarget as Node | null;
    const currentTarget = event.currentTarget as HTMLElement;
    if (!relatedTarget || !currentTarget.contains(relatedTarget)) {
      isDragging = false;
    }
  }

  async function handleDrop(event: DragEvent) {
    event.preventDefault();
    event.stopPropagation();
    isDragging = false;

    const files = event.dataTransfer?.files;
    if (!files || files.length === 0) return;

    const conversationId = get(claudeStore.activeConversationId);

    for (const file of files) {
      const filename = file.name;
      const extension = filename.split(".").pop()?.toLowerCase() || "";
      const fileType = getFileTypeFromExtension(extension);

      // Create attachment from dropped file
      // Note: For dropped files, we create a preview URL for images
      let previewUrl: string | undefined;
      if (fileType === "image") {
        previewUrl = URL.createObjectURL(file);
      }

      // Check if the file has a native path (from Tauri's file drop)
      // If not, we need to save it to a temp file
      let savedPath = (file as File & { path?: string }).path;

      if (!savedPath && conversationId) {
        try {
          const arrayBuffer = await file.arrayBuffer();
          const bytes = Array.from(new Uint8Array(arrayBuffer));
          const result = await invoke<{ path: string; filename: string }>("save_temp_file", {
            conversationId,
            filename,
            data: bytes,
          });
          savedPath = result.path;
        } catch (error) {
          console.error("Failed to save dropped file to temp:", error);
          savedPath = file.name;
        }
      }

      const attachment: Attachment = {
        id: `attachment-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
        filename,
        path: savedPath || file.name,
        size: file.size,
        type: fileType,
        mimeType: file.type,
        previewUrl,
      };

      claudeStore.addAttachment(attachment);
    }
  }

  async function handleFilePicker() {
    try {
      const selected = await open({
        multiple: true,
        filters: [
          {
            name: "All Files",
            extensions: ["*"],
          },
          {
            name: "Images",
            extensions: ["png", "jpg", "jpeg", "gif", "webp", "svg", "bmp"],
          },
          {
            name: "Documents",
            extensions: ["pdf", "txt", "md", "doc", "docx", "csv", "json", "xml"],
          },
          {
            name: "Code",
            extensions: [
              "js",
              "ts",
              "jsx",
              "tsx",
              "py",
              "rs",
              "go",
              "java",
              "c",
              "cpp",
              "h",
              "hpp",
              "css",
              "html",
              "svelte",
              "vue",
            ],
          },
        ],
      });

      if (!selected) return;

      // Handle both single and multiple file selection
      const files = Array.isArray(selected) ? selected : [selected];

      for (const filePath of files) {
        const filename = filePath.split(/[/\\]/).pop() || "unknown";
        const extension = filename.split(".").pop()?.toLowerCase() || "";
        const fileType = getFileTypeFromExtension(extension);

        // Get file size from Tauri
        let fileSize = 0;
        try {
          fileSize = await invoke<number>("get_file_size", { filePath });
        } catch (e) {
          console.warn("Could not get file size:", e);
        }

        const attachment: Attachment = {
          id: `attachment-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
          filename,
          path: filePath,
          size: fileSize,
          type: fileType,
        };

        claudeStore.addAttachment(attachment);
      }
    } catch (error) {
      console.error("Failed to open file picker:", error);
    }
  }

  async function handlePaste(event: ClipboardEvent) {
    // First, try the web clipboard API for files
    const items = event.clipboardData?.items;
    let handledFile = false;

    // Also capture text content to clipboard history
    const textContent = event.clipboardData?.getData("text/plain");
    if (textContent && textContent.trim().length > 0) {
      // Only capture multi-line or longer text (likely code snippets)
      if (textContent.includes("\n") || textContent.length > 50) {
        clipboardStore.captureClipboard(textContent, null, "Pasted into chat");
      }
    }

    if (items && items.length > 0) {
      for (const item of items) {
        if (item.kind === "file") {
          const file = item.getAsFile();
          if (!file) continue;

          event.preventDefault();
          handledFile = true;

          const filename = file.name || `pasted-${Date.now()}.${file.type.split("/")[1] || "png"}`;
          const extension = filename.split(".").pop()?.toLowerCase() || "";
          const fileType = getFileTypeFromExtension(extension);

          let previewUrl: string | undefined;
          if (fileType === "image" || file.type.startsWith("image/")) {
            previewUrl = URL.createObjectURL(file);
          }

          const conversationId = get(claudeStore.activeConversationId);
          let savedPath = filename;

          if (conversationId) {
            try {
              const arrayBuffer = await file.arrayBuffer();
              const bytes = Array.from(new Uint8Array(arrayBuffer));
              const result = await invoke<{ path: string; filename: string }>("save_temp_file", {
                conversationId,
                filename,
                data: bytes,
              });
              savedPath = result.path;
            } catch (error) {
              console.error("Failed to save pasted file to temp:", error);
            }
          }

          const attachment: Attachment = {
            id: `attachment-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
            filename,
            path: savedPath,
            size: file.size,
            type: file.type.startsWith("image/") ? "image" : fileType,
            mimeType: file.type,
            previewUrl,
          };

          claudeStore.addAttachment(attachment);
        }
      }
    }

    // If web clipboard didn't have files, try Tauri's native clipboard for images
    if (!handledFile) {
      try {
        const image = await readImage();
        const rgba = await image.rgba();
        const size = await image.size();

        if (rgba && rgba.length > 0) {
          event.preventDefault();

          const conversationId = get(claudeStore.activeConversationId);
          const filename = `screenshot-${Date.now()}.png`;

          // Convert RGBA to PNG using canvas
          const canvas = document.createElement("canvas");
          canvas.width = size.width;
          canvas.height = size.height;
          const ctx = canvas.getContext("2d");

          if (ctx) {
            const imgData = ctx.createImageData(size.width, size.height);
            imgData.data.set(new Uint8ClampedArray(rgba));
            ctx.putImageData(imgData, 0, 0);

            // Create preview URL from canvas
            const previewUrl = canvas.toDataURL("image/png");

            // Convert to blob for saving
            const blob = await new Promise<Blob | null>((resolve) =>
              canvas.toBlob(resolve, "image/png")
            );

            let savedPath = filename;
            if (blob && conversationId) {
              try {
                const arrayBuffer = await blob.arrayBuffer();
                const bytes = Array.from(new Uint8Array(arrayBuffer));
                const result = await invoke<{ path: string; filename: string }>("save_temp_file", {
                  conversationId,
                  filename,
                  data: bytes,
                });
                savedPath = result.path;
              } catch (error) {
                console.error("Failed to save clipboard image to temp:", error);
              }
            }

            const attachment: Attachment = {
              id: `attachment-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
              filename,
              path: savedPath,
              size: blob?.size || 0,
              type: "image",
              mimeType: "image/png",
              previewUrl,
            };

            claudeStore.addAttachment(attachment);
          }
        }
      } catch (error) {
        // No image in clipboard or clipboard read failed - that's fine, just ignore
        console.log("No image in native clipboard:", error);
      }
    }
  }

  function handleSnippetInsert(content: string): void {
    // Insert snippet at cursor position or append to input
    if (inputValue.trim()) {
      inputValue = inputValue + "\n\n" + content;
    } else {
      inputValue = content;
    }
    userHasTyped = true;
  }

  function handleClipboardInsert(content: string): void {
    // Insert clipboard content at cursor position or append to input
    if (inputValue.trim()) {
      inputValue = inputValue + "\n\n" + content;
    } else {
      inputValue = content;
    }
    userHasTyped = true;
  }

  async function handleQuickAction(prompt: string): Promise<void> {
    // Quick actions send the prompt directly
    if (!isConnected || isSubmitting) return;

    // Add to history
    addToHistory(prompt);
    historyIndex = -1;
    tempInput = "";
    userHasTyped = false;

    isSubmitting = true;

    // Reset notification state for new user message
    handleNewUserMessage();

    claudeStore.addLine("user", prompt);
    characterState.setState("thinking");

    try {
      const conversationId = get(claudeStore.activeConversationId);
      if (!conversationId) {
        throw new Error("No active conversation");
      }
      await invoke("send_prompt", {
        conversationId,
        message: prompt,
      });
    } catch (error) {
      console.error("Failed to send quick action:", error);
      claudeStore.addLine("error", `Failed to send: ${error}`);
      characterState.setTemporaryState("error", 3000);
    } finally {
      isSubmitting = false;
    }
  }

  function handleKeyDown(event: KeyboardEvent) {
    // Handle command menu navigation
    if (showCommandMenu && matchingCommands.length > 0) {
      if (event.key === "ArrowDown") {
        event.preventDefault();
        selectedCommandIndex = (selectedCommandIndex + 1) % matchingCommands.length;
        return;
      }
      if (event.key === "ArrowUp") {
        event.preventDefault();
        selectedCommandIndex =
          (selectedCommandIndex - 1 + matchingCommands.length) % matchingCommands.length;
        return;
      }
      if (event.key === "Tab") {
        event.preventDefault();
        const selected = matchingCommands[selectedCommandIndex];
        if (selected) {
          selectCommand(selected);
        }
        return;
      }
      if (event.key === "Escape") {
        event.preventDefault();
        showCommandMenu = false;
        return;
      }
    }

    // Handle input history navigation (when command menu is closed AND user hasn't typed)
    // If user has typed something, let arrow keys navigate the cursor instead
    if (event.key === "ArrowUp" && inputHistory.length > 0 && !userHasTyped) {
      event.preventDefault();
      if (historyIndex === -1) {
        // Save current input before navigating history
        tempInput = inputValue;
      }
      if (historyIndex < inputHistory.length - 1) {
        historyIndex++;
        inputValue = inputHistory[historyIndex];
      }
      return;
    }

    if (event.key === "ArrowDown" && historyIndex >= 0 && !userHasTyped) {
      event.preventDefault();
      historyIndex--;
      if (historyIndex === -1) {
        // Restore the temp input when going back to current
        inputValue = tempInput;
        userHasTyped = false; // Reset since we're back to empty/temp state
      } else {
        inputValue = inputHistory[historyIndex];
      }
      return;
    }

    if (event.key === "Enter" && !event.shiftKey) {
      handleSubmit(event);
    }
  }
</script>

<form
  onsubmit={handleSubmit}
  ondragenter={handleDragEnter}
  ondragover={handleDragOver}
  ondragleave={handleDragLeave}
  ondrop={handleDrop}
  class="input-bar"
  class:is-dragging={isDragging}
>
  <AttachmentPreview {attachments} onRemove={handleRemoveAttachment} />

  <div class="input-controls flex gap-2 mb-2">
    <MessageModeSelector />
    <button
      type="button"
      onclick={() => configStore.toggleStreamerMode()}
      class="control-button streamer-toggle"
      class:streamer-active={streamerModeActive}
      title="Toggle Streamer Mode (Ctrl+Shift+S)"
    >
      {#if streamerModeActive}
        <div class="live-indicator"></div>
        <span>LIVE</span>
      {:else}
        <svg
          width="16"
          height="16"
          viewBox="0 0 24 24"
          fill="none"
          stroke="currentColor"
          stroke-width="2"
          stroke-linecap="round"
          stroke-linejoin="round"
        >
          <path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z" />
          <circle cx="12" cy="12" r="3" />
          <line x1="1" y1="1" x2="23" y2="23" />
        </svg>
        <span>Stream</span>
      {/if}
    </button>
    <button
      type="button"
      onclick={() => (showQuickActions = true)}
      class="control-button"
      title="Quick Actions"
    >
      <svg
        width="16"
        height="16"
        viewBox="0 0 24 24"
        fill="none"
        stroke="currentColor"
        stroke-width="2"
        stroke-linecap="round"
        stroke-linejoin="round"
      >
        <path d="M13 10V3L4 14h7v7l9-11h-7z" />
      </svg>
      <span>Actions</span>
    </button>
    <button
      type="button"
      onclick={() => (showSnippetLibrary = true)}
      class="control-button"
      title="Snippet Library"
    >
      <svg
        width="16"
        height="16"
        viewBox="0 0 24 24"
        fill="none"
        stroke="currentColor"
        stroke-width="2"
        stroke-linecap="round"
        stroke-linejoin="round"
      >
        <path d="M14.5 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7.5L14.5 2z" />
        <polyline points="14,2 14,8 20,8" />
        <line x1="16" y1="13" x2="8" y2="13" />
        <line x1="16" y1="17" x2="8" y2="17" />
        <line x1="10" y1="9" x2="8" y2="9" />
      </svg>
      <span>Snippets</span>
    </button>
    <button
      type="button"
      onclick={() => (showClipboardHistory = true)}
      class="control-button"
      title="Clipboard History"
    >
      <svg
        width="16"
        height="16"
        viewBox="0 0 24 24"
        fill="none"
        stroke="currentColor"
        stroke-width="2"
        stroke-linecap="round"
        stroke-linejoin="round"
      >
        <path d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2" />
        <rect x="9" y="3" width="6" height="4" rx="1" />
      </svg>
      <span>Clipboard</span>
    </button>

    <CliVersion />
    <SystemClock />
  </div>

  <div class="input-row">
    <div class="textarea-wrapper">
      <SlashCommandMenu
        commands={matchingCommands}
        selectedIndex={selectedCommandIndex}
        onSelect={selectCommand}
      />
      <!-- svelte-ignore a11y_no_static_element_interactions -->
      <div class="resize-handle" onmousedown={handleResizeStart} title="Drag to resize"></div>
      <textarea
        bind:this={textareaElement}
        bind:value={inputValue}
        onkeydown={handleKeyDown}
        oninput={handleInputChange}
        onpaste={handlePaste}
        oncontextmenu={handleContextMenu}
        placeholder={isConnected
          ? "Ask Hikari anything... (type / for commands)"
          : "Connect to Claude first..."}
        disabled={isSubmitting}
        rows={1}
        style="height: {textareaHeight}px; font-size: var(--terminal-font-size, 14px);"
        class="w-full px-4 py-3 bg-[var(--bg-secondary)] border border-[var(--border-color)]
             rounded-lg text-[var(--text-primary)] placeholder-gray-500 resize-none
             input-trans-focus disabled:opacity-50 disabled:cursor-not-allowed"
      ></textarea>
    </div>

    <div class="button-wrapper">
      {#if costEstimate && isConnected && !isProcessing}
        <div class="cost-estimate" title="Estimated input cost for this message">
          <span class="cost-tokens">+{formatTokenCount(costEstimate.messageTokens)}</span>
          <span class="cost-value">${costEstimate.estimatedCost.toFixed(4)}</span>
        </div>
      {/if}

      <button type="button" onclick={handleFilePicker} class="attach-button" title="Attach files">
        <svg
          width="20"
          height="20"
          viewBox="0 0 24 24"
          fill="none"
          stroke="currentColor"
          stroke-width="2"
          stroke-linecap="round"
          stroke-linejoin="round"
        >
          <path
            d="M21.44 11.05l-9.19 9.19a6 6 0 0 1-8.49-8.49l9.19-9.19a4 4 0 0 1 5.66 5.66l-9.2 9.19a2 2 0 0 1-2.83-2.83l8.49-8.48"
          />
        </svg>
      </button>

      {#if isProcessing}
        <button
          type="button"
          onclick={handleInterrupt}
          class="send-button btn-trans-gradient"
          title="Interrupt the current response (Ctrl+C)"
        >
          <span class="font-bold">■</span> Stop
        </button>
      {:else}
        <button
          type="submit"
          disabled={!isConnected ||
            isSubmitting ||
            (!inputValue.trim() && attachments.length === 0)}
          class="send-button trans-gradient-button
               disabled:opacity-50 disabled:cursor-not-allowed"
        >
          {#if isSubmitting}
            <span class="inline-block animate-spin">⏳</span>
          {:else}
            Send
          {/if}
        </button>
      {/if}
    </div>
  </div>
</form>

{#if showSnippetLibrary}
  <SnippetLibraryPanel
    onClose={() => (showSnippetLibrary = false)}
    onInsert={handleSnippetInsert}
  />
{/if}

{#if showQuickActions}
  <QuickActionsPanel onClose={() => (showQuickActions = false)} onAction={handleQuickAction} />
{/if}

{#if showClipboardHistory}
  <ClipboardHistoryPanel
    isOpen={showClipboardHistory}
    onClose={() => (showClipboardHistory = false)}
    onInsert={handleClipboardInsert}
  />
{/if}

{#if contextMenuShow && textareaElement}
  <TextInputContextMenu
    x={contextMenuX}
    y={contextMenuY}
    inputElement={textareaElement}
    onClose={closeContextMenu}
  />
{/if}

<style>
  .input-bar {
    display: flex;
    flex-direction: column;
    gap: 8px;
    position: relative;
    transition:
      border-color 0.2s,
      background 0.2s;
  }

  .input-bar.is-dragging {
    background: var(--bg-secondary);
    border: 2px dashed var(--accent-primary);
    border-radius: 12px;
    padding: 8px;
  }

  .input-bar.is-dragging::before {
    content: "Drop files here";
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    font-size: 18px;
    font-weight: 500;
    color: var(--accent-primary);
    pointer-events: none;
    z-index: 10;
  }

  .input-bar.is-dragging > * {
    opacity: 0.3;
    pointer-events: none;
  }

  .input-controls {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
  }

  .control-button {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    padding: 10px 16px;
    background: var(--bg-secondary);
    border: 1px solid var(--border-color);
    border-radius: 8px;
    color: var(--text-secondary);
    cursor: pointer;
    transition: all 0.2s;
    font-size: 14px;
    white-space: nowrap;
    flex-shrink: 0;
  }

  /* Hide button text on smaller screens, show icons only */
  @media (max-width: 640px) {
    .control-button span {
      display: none;
    }
    .control-button {
      padding: 10px;
      min-width: 40px;
    }
  }

  .control-button:hover {
    background: var(--bg-tertiary);
    border-color: var(--accent-primary);
    color: var(--accent-primary);
  }

  .control-button:active {
    transform: scale(0.95);
  }

  .streamer-toggle.streamer-active {
    background: rgba(239, 68, 68, 0.2);
    border-color: rgb(239, 68, 68);
    color: rgb(248, 113, 113);
    animation: pulse-red 2s ease-in-out infinite;
  }

  .streamer-toggle.streamer-active:hover {
    background: rgba(239, 68, 68, 0.3);
    border-color: rgb(248, 113, 113);
  }

  .live-indicator {
    width: 8px;
    height: 8px;
    border-radius: 50%;
    background: rgb(239, 68, 68);
    animation: pulse-dot 1.5s ease-in-out infinite;
  }

  @keyframes pulse-red {
    0%,
    100% {
      box-shadow: 0 0 0 0 rgba(239, 68, 68, 0.4);
    }
    50% {
      box-shadow: 0 0 0 4px rgba(239, 68, 68, 0);
    }
  }

  @keyframes pulse-dot {
    0%,
    100% {
      opacity: 1;
    }
    50% {
      opacity: 0.5;
    }
  }

  .input-row {
    display: flex;
    gap: 12px;
    align-items: flex-end;
  }

  .textarea-wrapper {
    flex: 1;
    position: relative;
    display: flex;
    flex-direction: column;
  }

  .resize-handle {
    height: 6px;
    cursor: ns-resize;
    background: transparent;
    border-radius: 3px;
    margin-bottom: 2px;
    display: flex;
    justify-content: center;
    align-items: center;
  }

  .resize-handle::before {
    content: "";
    width: 40px;
    height: 3px;
    background: var(--border-color);
    border-radius: 2px;
    opacity: 0.5;
    transition: opacity 0.2s;
  }

  .resize-handle:hover::before {
    opacity: 1;
    background: var(--accent-primary);
  }

  .button-wrapper {
    display: flex;
    align-items: flex-end;
    height: 100%;
    gap: 8px;
  }

  .cost-estimate {
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    justify-content: center;
    padding: 0 8px;
    font-size: 0.7rem;
    color: var(--text-secondary);
    min-width: 60px;
    height: 48px;
  }

  .cost-tokens {
    opacity: 0.8;
  }

  .cost-value {
    font-family: var(--font-mono, monospace);
    color: var(--accent-primary);
  }

  .attach-button {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 48px;
    height: 48px;
    padding: 0;
    background: var(--bg-secondary);
    border: 1px solid var(--border-color);
    border-radius: 8px;
    color: var(--text-secondary);
    cursor: pointer;
    transition: all 0.2s;
  }

  .attach-button:hover {
    background: var(--bg-tertiary);
    border-color: var(--accent-primary);
    color: var(--accent-primary);
    transform: scale(1.05);
  }

  .attach-button:active {
    transform: scale(0.95);
  }

  .send-button {
    padding: 0 24px;
    height: 48px;
    color: white;
    font-weight: 500;
    border-radius: 8px;
    transition: all 0.2s;
    white-space: nowrap;
  }

  .send-button:hover:not(:disabled) {
    transform: scale(1.05);
  }

  .send-button:active:not(:disabled) {
    transform: scale(0.95);
  }

  .trans-gradient-button {
    background: var(--trans-gradient-vibrant);
    border: none;
    color: #1a1a2e;
    font-weight: 600;
    text-shadow: 0 0 2px rgba(255, 255, 255, 0.5);
  }

  .trans-gradient-button:hover:not(:disabled) {
    filter: brightness(1.1);
    box-shadow:
      0 0 20px rgba(91, 206, 250, 0.4),
      0 0 30px rgba(245, 169, 184, 0.3);
  }
</style>
