<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 InputDialog from "./lib/components/ui/InputDialog.svelte";
  import TemplateDialog from "./lib/components/ui/TemplateDialog.svelte";
  import {
    createCsoundDerivedStores,
    type LogEntry,
    type EvalSource,
  } from "./lib/csound";
  import { type CsoundProject } from "./lib/project-system";
  import {
    templateRegistry,
    type CsoundTemplate,
  } from "./lib/templates/template-registry";
  import { loadLastProjectId } from "./lib/project-system/persistence";
  import { createAppContext, setAppContext } from "./lib/contexts/app-context";
  import type { ProjectMode } from "./lib/project-system/types";
  import {
    Save,
    Share2,
    Activity,
    FileStack,
    PanelLeftOpen,
    PanelRightOpen,
    CircleStop,
  } from "lucide-svelte";

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

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

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

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

  onMount(async () => {
    await projectManager.init();

    const lastProjectId = loadLastProjectId();
    let projectToLoad: CsoundProject | null = null;

    if (lastProjectId) {
      const result = await projectManager.getProject(lastProjectId);
      if (result.success) {
        projectToLoad = result.data;
      }
    }

    if (!projectToLoad) {
      const allProjectsResult = await projectManager.getAllProjects();
      if (allProjectsResult.success) {
        if (allProjectsResult.data.length > 0) {
          projectToLoad = allProjectsResult.data[0];
        } else {
          const classicTemplate = templateRegistry.getById("classic");
          if (classicTemplate) {
            const createResult = await projectManager.createProject({
              title: "Welcome",
              author: "System",
              content: classicTemplate.content,
              tags: [],
              mode: classicTemplate.mode,
            });
            if (createResult.success) {
              projectToLoad = createResult.data;
            }
          }
        }
      }
    }

    if (projectToLoad) {
      projectEditor.loadProject(projectToLoad);
    }

    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) {
    projectEditor.setContent(value);
  }

  async function handleNewEmptyFile() {
    const emptyTemplate = templateRegistry.getEmpty();
    const result = projectEditor.requestSwitch(async () =>
      await projectEditor.createNew(emptyTemplate.content, emptyTemplate.mode),
    );

    if (result === "confirm-unsaved") {
      uiState.showUnsavedChangesDialog();
    }
  }

  function handleNewFromTemplate() {
    uiState.showTemplateDialog();
  }

  async function handleTemplateSelect(template: CsoundTemplate) {
    uiState.hideTemplateDialog();

    const result = projectEditor.requestSwitch(async () =>
      await projectEditor.createNew(template.content, template.mode),
    );

    if (result === "confirm-unsaved") {
      uiState.showUnsavedChangesDialog();
    }
  }

  function handleFileSelect(project: CsoundProject | null) {
    if (!project) return;

    const result = projectEditor.requestSwitch(() =>
      projectEditor.loadProject(project),
    );

    if (result === "confirm-unsaved") {
      uiState.showUnsavedChangesDialog();
    }
  }

  async function handleExecute(code: string, source: EvalSource) {
    try {
      await executionContext.execute(code, source);
    } catch (error) {
      console.error("Execution error:", error);
    }
  }

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

  async function handleSaveAs(title: string) {
    const success = await projectEditor.saveAs(title);
    if (success) {
      uiState.hideSaveAsDialog();
    }
  }

  async function handleMetadataUpdate(
    projectId: string,
    updates: {
      title?: string;
      author?: string;
      mode?: import("./lib/project-system/types").ProjectMode;
    },
  ) {
    await projectEditor.updateMetadata(updates);
  }

  async function handleSwitchSave() {
    await projectEditor.confirmSaveAndSwitch();
    uiState.hideUnsavedChangesDialog();
  }

  function handleSwitchDiscard() {
    projectEditor.confirmDiscardAndSwitch();
    uiState.hideUnsavedChangesDialog();
  }

  async function handleShare() {
    if (!projectEditor.currentProjectId) return;

    const result = await projectManager.exportProjectToUrl(
      projectEditor.currentProjectId,
    );
    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(() => {
    if (uiState.scopePopupVisible || uiState.spectrogramPopupVisible) {
      analyserNode = csound.getAnalyserNode();

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

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

  $effect(() => {
    const mode = projectEditor.currentProject?.mode || "composition";
    const projectId = projectEditor.currentProjectId;

    if (mode !== executionContext.mode) {
      executionContext.switchMode(mode).catch(console.error);
    }

    executionContext.setContentProvider(() => projectEditor.content);

    if (projectId) {
      executionContext.startSession(projectId).catch(console.error);
    }
  });

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

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

{#snippet filesTabContent()}
  <FileBrowser
    {projectManager}
    onFileSelect={handleFileSelect}
    onNewEmptyFile={handleNewEmptyFile}
    onNewFromTemplate={handleNewFromTemplate}
    onMetadataUpdate={handleMetadataUpdate}
    selectedProjectId={projectEditor.currentProjectId}
  />
{/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={handleNewFromTemplate}
        title="New from template"
      >
        <FileStack size={18} />
      </button>
      <button
        class="icon-button"
        onclick={handleSave}
        disabled={!projectEditor.hasUnsavedChanges}
        title="Save (Ctrl+S){projectEditor.hasUnsavedChanges
          ? ' - unsaved changes'
          : ''}"
        class:has-changes={projectEditor.hasUnsavedChanges}
      >
        <Save size={18} />
      </button>
      <button
        onclick={handleShare}
        class="icon-button"
        disabled={!projectEditor.currentProjectId}
        title="Share project"
      >
        <Share2 size={18} />
      </button>
    {/snippet}
    <button
      onclick={() => uiState.openScope()}
      class="icon-button"
      title="Open audio scope"
    >
      <Activity size={18} />
    </button>
    <button
      onclick={() => uiState.openSpectrogram()}
      class="icon-button"
      title="Open 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
        value={projectEditor.content}
        onChange={handleEditorChange}
        onExecute={handleExecute}
        logs={interpreterLogs}
        {editorSettings}
        mode={projectEditor.currentProject?.mode || 'composition'}
      />
    </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>

  <ConfirmDialog
    bind:visible={uiState.unsavedChangesDialogVisible}
    title="Unsaved Changes"
    message="You have unsaved changes. What would you like to do?"
    confirmLabel="Save"
    cancelLabel="Discard"
    onConfirm={handleSwitchSave}
    onCancel={handleSwitchDiscard}
  />

  <InputDialog
    bind:visible={uiState.saveAsDialogVisible}
    title="Save As"
    label="File name"
    placeholder="Untitled"
    onConfirm={handleSaveAs}
  />

  <TemplateDialog
    bind:visible={uiState.templateDialogVisible}
    onSelect={handleTemplateSelect}
    onCancel={() => uiState.hideTemplateDialog()}
  />
</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: 0.5rem;
    background-color: #333;
    color: rgba(255, 255, 255, 0.87);
    border: 1px solid #555;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    transition: background-color 0.2s;
  }

  .icon-button:hover:not(:disabled) {
    background-color: #444;
    border-color: #646cff;
  }

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

  .icon-button.has-changes {
    color: #646cff;
  }

  .icon-button.stop-button:not(:disabled) {
    color: #ff6b6b;
  }

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

  h3 {
    margin-top: 0;
    color: rgba(255, 255, 255, 0.87);
  }

  p {
    color: rgba(255, 255, 255, 0.7);
    line-height: 1.6;
  }

  .share-content {
    display: flex;
    flex-direction: column;
    gap: 1rem;
  }

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

  .share-url-input {
    width: 100%;
    padding: 0.75rem;
    background-color: #2a2a2a;
    border: 1px solid #3a3a3a;
    color: rgba(255, 255, 255, 0.87);
    font-size: 0.875rem;
    font-family: monospace;
    outline: none;
    box-sizing: border-box;
  }

  .share-url-input:focus {
    border-color: #646cff;
  }

  .share-instructions {
    font-size: 0.875rem;
    color: rgba(255, 255, 255, 0.5);
    margin: 0;
  }

  .audio-permission-content {
    display: flex;
    flex-direction: column;
    gap: 1rem;
    align-items: center;
    text-align: center;
    padding: 1rem;
  }

  .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: 1rem;
    padding: 0.75rem 2rem;
    background-color: #646cff;
    color: white;
    border: none;
    font-size: 1rem;
    font-weight: 600;
    cursor: pointer;
    transition: background-color 0.2s;
  }

  .enable-audio-button:hover {
    background-color: #535bdb;
  }

  .enable-audio-button:active {
    background-color: #424ab8;
  }
</style>
