<script lang="ts">
  import { onMount, onDestroy } from "svelte";
  import TopBar from "./lib/components/ui/TopBar.svelte";
  import EditorWithLogs from "./lib/components/editor/EditorWithLogs.svelte";
  import EditorSettings from "./lib/components/editor/EditorSettings.svelte";
  import FileBrowser from "./lib/components/ui/FileBrowser.svelte";
  import SidePanel from "./lib/components/ui/SidePanel.svelte";
  import ResizablePopup from "./lib/components/ui/ResizablePopup.svelte";
  import AudioScope from "./lib/components/audio/AudioScope.svelte";
  import Spectrogram from "./lib/components/audio/Spectrogram.svelte";
  import ConfirmDialog from "./lib/components/ui/ConfirmDialog.svelte";
  import CsoundReference from "./lib/components/reference/CsoundReference.svelte";
  import {
    createCsoundDerivedStores,
    type LogEntry,
    type EvalSource,
  } from "./lib/csound";
  import { type File } from "./lib/project-system";
  import { loadOpenTabs, loadCurrentFileId } from "./lib/project-system/persistence";
  import { createAppContext, setAppContext } from "./lib/contexts/app-context";
  import { themes, applyTheme } from "./lib/themes";
  import {
    Save,
    Share2,
    Activity,
    FileStack,
    PanelLeftOpen,
    PanelRightOpen,
    CircleStop,
    Play,
  } from "lucide-svelte";

  const appContext = createAppContext();
  setAppContext(appContext);

  const {
    csound,
    fileManager,
    editorSettings,
    editorState,
    uiState,
    executionContext,
  } = appContext;
  const { logs: csoundLogs, initialized, compiled, running } = createCsoundDerivedStores(csound);

  let analyserNode = $state<AnalyserNode | null>(null);
  let interpreterLogs = $state<LogEntry[]>([]);
  let editorWithLogsRef: EditorWithLogs;

  let logsUnsubscribe: (() => void) | undefined;

  onMount(async () => {
    await fileManager.init();
    await editorState.refreshFileCache();

    // Try to restore open tabs
    const openTabIds = loadOpenTabs();
    const currentFileId = loadCurrentFileId();

    if (openTabIds.length > 0) {
      // Restore tabs
      for (const fileId of openTabIds) {
        await editorState.openFile(fileId);
      }

      // Switch to last current file
      if (currentFileId && openTabIds.includes(currentFileId)) {
        await editorState.switchToFile(currentFileId);
      }
    } else {
      // No tabs to restore, check if we have any files
      const allFilesResult = await fileManager.getAllFiles();
      if (allFilesResult.success && allFilesResult.data.length > 0) {
        // Open the first file
        await editorState.openFile(allFilesResult.data[0].id);
      } else {
        // No files at all, create a welcome file
        const welcomeContent = '<CsoundSynthesizer>\n<CsOptions>\n-odac\n</CsOptions>\n<CsInstruments>\n\nsr = 44100\nksmps = 32\nnchnls = 2\n0dbfs = 1\n\ninstr 1\n  kEnv madsr 0.1, 0.2, 0.7, 0.5\n  aOut oscil kEnv * 0.3, 440\n  outs aOut, aOut\nendin\n\n</CsInstruments>\n<CsScore>\ni 1 0 2\n</CsScore>\n</CsoundSynthesizer>';
        const result = await fileManager.createFile({ title: 'Welcome.orc', content: welcomeContent });
        if (result.success) {
          await editorState.openFile(result.data.id);
        }
      }
    }

    logsUnsubscribe = csoundLogs.subscribe((logs) => {
      interpreterLogs = logs;
    });

    const handleKeyDown = (e: KeyboardEvent) => {
      if ((e.ctrlKey || e.metaKey) && e.key === '.') {
        e.preventDefault();
        handleStop();
      }

      if ((e.ctrlKey || e.metaKey) && e.key === 's') {
        e.preventDefault();
        handleSave();
      }
    };

    window.addEventListener('keydown', handleKeyDown);

    return () => {
      window.removeEventListener('keydown', handleKeyDown);
    };
  });

  onDestroy(async () => {
    logsUnsubscribe?.();
    await csound.destroy();
  });

  async function handleEnableAudio() {
    try {
      await csound.init();
      uiState.closeAudioPermission();
    } catch (error) {
      console.error("Failed to initialize audio:", error);
    }
  }

  function handleEditorChange(value: string) {
    editorState.setContent(value);
  }

  async function handleNewFile() {
    await editorState.createNewFile();
  }

  async function handleFileOpen(fileId: string) {
    await editorState.openFile(fileId);
  }

  async function handleFileDelete(fileId: string) {
    await editorState.deleteFile(fileId);
  }

  async function handleFileRename(fileId: string, newTitle: string) {
    await editorState.renameFile(fileId, newTitle);
  }

  async function handleTabClick(fileId: string) {
    await editorState.switchToFile(fileId);
  }

  async function handleTabClose(fileId: string) {
    await editorState.closeFile(fileId);
  }

  async function handleExecuteFile() {
    try {
      await executionContext.executeFile();
    } catch (error) {
      console.error("Execution error:", error);
    }
  }

  async function handleExecuteBlock(code: string) {
    try {
      await executionContext.executeBlock(code);
    } catch (error) {
      console.error("Execution error:", error);
    }
  }

  async function handleExecuteSelection(code: string) {
    try {
      await executionContext.executeSelection(code);
    } catch (error) {
      console.error("Execution error:", error);
    }
  }

  async function handleSave() {
    await editorState.save();
  }

  async function handleShare() {
    if (!editorState.currentFileId) return;

    const result = await fileManager.exportFilesToUrl([editorState.currentFileId]);
    if (result.success) {
      try {
        await navigator.clipboard.writeText(result.data);
      } catch (err) {
        console.error("Failed to copy to clipboard:", err);
      }
      uiState.showShare(result.data);
    }
  }

  async function handleStop() {
    try {
      await csound.stop();
    } catch (error) {
      console.error("Failed to stop:", error);
    }
  }

  $effect(() => {
    const theme = themes[$editorSettings.theme];
    if (theme) {
      applyTheme(theme);
    }
  });

  $effect(() => {
    if (uiState.scopePopupVisible || uiState.spectrogramPopupVisible) {
      analyserNode = csound.getAnalyserNode();

      const unsubscribe = csound.onAnalyserNodeCreated((node) => {
        analyserNode = node;
      });

      return unsubscribe;
    } else {
      analyserNode = null;
    }
  });

  $effect(() => {
    executionContext.setContentProvider(() => editorState.content);
  });

  const panelTabs = [
    {
      id: "editor",
      label: "Editor",
      content: editorTabContent,
    },
    {
      id: "files",
      label: "Files",
      content: filesTabContent,
    },
    {
      id: "reference",
      label: "Reference",
      content: referenceTabContent,
    },
  ];
</script>

{#snippet editorTabContent()}
  <EditorSettings />
{/snippet}

{#snippet filesTabContent()}
  <FileBrowser
    {fileManager}
    onFileOpen={handleFileOpen}
    onFileDelete={handleFileDelete}
    onFileRename={handleFileRename}
    onNewFile={handleNewFile}
  />
{/snippet}

{#snippet referenceTabContent()}
  <CsoundReference />
{/snippet}

<div class="app-container">
  <TopBar title="OldBoy">
    {#snippet leftActions()}
      <button
        onclick={handleStop}
        class="icon-button stop-button"
        disabled={!$running}
        title="Stop audio (Ctrl+.)"
      >
        <CircleStop size={18} />
      </button>
      <button
        class="icon-button"
        onclick={handleNewFile}
        title="New file"
      >
        <FileStack size={18} />
      </button>
      <button
        class="icon-button"
        onclick={handleSave}
        disabled={!editorState.hasUnsavedChanges}
        title="Save (Ctrl+S){editorState.hasUnsavedChanges
          ? ' - unsaved changes'
          : ''}"
        class:has-changes={editorState.hasUnsavedChanges}
      >
        <Save size={18} />
      </button>
      <button
        onclick={handleShare}
        class="icon-button"
        disabled={!editorState.currentFileId}
        title="Share current file"
      >
        <Share2 size={18} />
      </button>
    {/snippet}
    <button
      onclick={handleExecuteFile}
      class="icon-button evaluate-button"
      disabled={!$initialized}
      class:is-running={$running}
      title="Run File (Cmd-R)"
    >
      <Play size={18} />
    </button>
    <button
      onclick={() => uiState.toggleScope()}
      class="icon-button"
      title="Toggle audio scope"
    >
      <Activity size={18} />
    </button>
    <button
      onclick={() => uiState.toggleSpectrogram()}
      class="icon-button"
      title="Toggle spectrogram"
    >
      <svg
        width="18"
        height="18"
        viewBox="0 0 24 24"
        fill="none"
        stroke="currentColor"
        stroke-width="2"
      >
        <rect x="3" y="3" width="18" height="18" rx="2" />
        <path d="M3 9h18" />
        <path d="M3 15h18" />
        <path d="M9 3v18" />
        <path d="M15 3v18" />
      </svg>
    </button>
    {#if !uiState.sidePanelVisible}
      <button
        class="icon-button"
        onclick={() => uiState.toggleSidePanel()}
        title="Open side panel"
      >
        {#if uiState.sidePanelPosition === "left"}
          <PanelLeftOpen size={18} />
        {:else}
          <PanelRightOpen size={18} />
        {/if}
      </button>
    {/if}
  </TopBar>

  <div class="main-content">
    {#if uiState.sidePanelPosition === "left"}
      <SidePanel
        bind:visible={uiState.sidePanelVisible}
        bind:position={uiState.sidePanelPosition}
        tabs={panelTabs}
        onClose={() => uiState.toggleSidePanel()}
        onCyclePosition={() => uiState.cyclePanelPosition()}
      />
    {/if}

    <div class="editor-area">
      <EditorWithLogs
        bind:this={editorWithLogsRef}
        value={editorState.content}
        fileName={editorState.currentFile?.title}
        onChange={handleEditorChange}
        onExecuteFile={handleExecuteFile}
        onExecuteBlock={handleExecuteBlock}
        onExecuteSelection={handleExecuteSelection}
        logs={interpreterLogs}
        {editorSettings}
        openFiles={editorState.openFiles}
        currentFileId={editorState.currentFileId}
        unsavedFileIds={new Set(Array.from(editorState.unsavedChanges.keys()))}
        onTabClick={handleTabClick}
        onTabClose={handleTabClose}
      />
    </div>

    {#if uiState.sidePanelPosition === "right"}
      <SidePanel
        bind:visible={uiState.sidePanelVisible}
        bind:position={uiState.sidePanelPosition}
        tabs={panelTabs}
        onClose={() => uiState.toggleSidePanel()}
        onCyclePosition={() => uiState.cyclePanelPosition()}
      />
    {/if}
  </div>

  <ResizablePopup
    bind:visible={uiState.sharePopupVisible}
    title="Share Project"
    x={typeof window !== "undefined" ? window.innerWidth / 2 - 300 : 300}
    y={typeof window !== "undefined" ? window.innerHeight / 2 - 100 : 200}
    width={600}
    height={200}
    minWidth={400}
    minHeight={150}
  >
    {#snippet children()}
      <div class="share-content">
        <p>Link copied to clipboard!</p>
        <div class="share-url-container">
          <input
            type="text"
            readonly
            value={uiState.shareUrl}
            class="share-url-input"
            onclick={(e) => e.currentTarget.select()}
          />
        </div>
        <p class="share-instructions">
          Anyone with this link can import the project.
        </p>
      </div>
    {/snippet}
  </ResizablePopup>

  <ResizablePopup
    visible={uiState.audioPermissionPopupVisible}
    title="Audio Permission Required"
    x={typeof window !== "undefined" ? window.innerWidth / 2 - 250 : 250}
    y={typeof window !== "undefined" ? window.innerHeight / 2 - 175 : 200}
    width={500}
    height={270}
    minWidth={400}
    minHeight={300}
    closable={false}
  >
    {#snippet children()}
      <div class="audio-permission-content">
        <h3>Enable Audio Context</h3>
        <p>OldBoy needs permission to use audio playback.</p>
        <button class="enable-audio-button" onclick={handleEnableAudio}>
          Enable Audio
        </button>
      </div>
    {/snippet}
  </ResizablePopup>

  <ResizablePopup
    bind:visible={uiState.scopePopupVisible}
    title="Audio Scope"
    x={typeof window !== "undefined" ? window.innerWidth / 2 - 400 : 100}
    y={typeof window !== "undefined" ? window.innerHeight / 2 - 300 : 100}
    width={800}
    height={600}
    minWidth={400}
    minHeight={300}
    noPadding={true}
  >
    {#snippet children()}
      <AudioScope {analyserNode} />
    {/snippet}
  </ResizablePopup>

  <ResizablePopup
    bind:visible={uiState.spectrogramPopupVisible}
    title="Spectrogram"
    x={typeof window !== "undefined" ? window.innerWidth / 2 - 400 : 150}
    y={typeof window !== "undefined" ? window.innerHeight / 2 - 300 : 150}
    width={800}
    height={600}
    minWidth={400}
    minHeight={300}
    noPadding={true}
  >
    {#snippet children()}
      <Spectrogram {analyserNode} />
    {/snippet}
  </ResizablePopup>

</div>

<style>
  .app-container {
    display: flex;
    flex-direction: column;
    height: 100vh;
    width: 100vw;
    overflow: hidden;
  }

  .main-content {
    display: flex;
    flex: 1;
    overflow: hidden;
  }

  .editor-area {
    flex: 1;
    overflow: hidden;
  }

  .icon-button {
    padding: var(--space-sm);
    background-color: var(--border-color);
    color: var(--text-color);
    border: 1px solid var(--border-color);
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    transition: background-color var(--transition-base);
  }

  .icon-button:hover:not(:disabled) {
    background-color: var(--button-hover);
    border-color: var(--accent-color);
  }

  .icon-button:disabled {
    opacity: 0.3;
    cursor: not-allowed;
  }

  .icon-button.has-changes {
    color: var(--accent-color);
  }

  .icon-button.stop-button:not(:disabled) {
    color: var(--danger-color);
  }

  .icon-button.stop-button:hover:not(:disabled) {
    color: var(--danger-hover);
    border-color: var(--danger-hover);
  }

  .icon-button.evaluate-button.is-running {
    color: var(--accent-color);
  }

  .icon-button.evaluate-button.is-running:hover:not(:disabled) {
    border-color: var(--accent-color);
  }

  h3 {
    margin-top: 0;
    color: var(--text-color);
  }

  p {
    color: var(--text-secondary);
    line-height: 1.6;
  }

  .share-content {
    display: flex;
    flex-direction: column;
    gap: var(--space-lg);
  }

  .share-url-container {
    width: 100%;
  }

  .share-url-input {
    width: 100%;
    padding: var(--space-md);
    background-color: var(--surface-color);
    border: 1px solid var(--border-color);
    color: var(--text-color);
    font-size: var(--font-base);
    font-family: monospace;
    outline: none;
    box-sizing: border-box;
  }

  .share-url-input:focus {
    border-color: var(--accent-color);
  }

  .share-instructions {
    font-size: var(--font-base);
    color: var(--text-secondary);
    margin: 0;
  }

  .audio-permission-content {
    display: flex;
    flex-direction: column;
    gap: var(--space-lg);
    align-items: center;
    text-align: center;
    padding: var(--space-lg);
  }

  .audio-permission-content h3 {
    margin: 0;
    font-size: 1.25rem;
  }

  .audio-permission-content p {
    margin: 0;
    font-size: 0.95rem;
  }

  .enable-audio-button {
    margin-top: var(--space-lg);
    padding: var(--space-md) var(--space-2xl);
    background-color: var(--accent-color);
    color: white;
    border: none;
    font-size: var(--font-lg);
    font-weight: 600;
    cursor: pointer;
    transition: background-color var(--transition-base);
  }

  .enable-audio-button:hover {
    background-color: var(--accent-hover);
  }

  .enable-audio-button:active {
    background-color: var(--accent-color);
  }
</style>
