<script lang="ts">
  import { onMount, onDestroy } from "svelte";
  import { EditorView, basicSetup } from "codemirror";
  import { EditorState, Compartment } from "@codemirror/state";
  import { keymap } from "@codemirror/view";
  import { oneDark } from "@codemirror/theme-one-dark";
  import { javascript } from "@codemirror/lang-javascript";
  import { python } from "@codemirror/lang-python";
  import { rust } from "@codemirror/lang-rust";
  import { html } from "@codemirror/lang-html";
  import { css } from "@codemirror/lang-css";
  import { json } from "@codemirror/lang-json";
  import { markdown } from "@codemirror/lang-markdown";
  import { xml } from "@codemirror/lang-xml";
  import { sql } from "@codemirror/lang-sql";
  import { java } from "@codemirror/lang-java";
  import { cpp } from "@codemirror/lang-cpp";
  import { php } from "@codemirror/lang-php";
  import { go } from "@codemirror/lang-go";
  import { yaml } from "@codemirror/lang-yaml";
  import { sass } from "@codemirror/lang-sass";
  import { less } from "@codemirror/lang-less";
  import { vue } from "@codemirror/lang-vue";
  import { angular } from "@codemirror/lang-angular";
  import { wast } from "@codemirror/lang-wast";
  import { StreamLanguage } from "@codemirror/language";
  import { shell } from "@codemirror/legacy-modes/mode/shell";
  import { ruby } from "@codemirror/legacy-modes/mode/ruby";
  import { swift } from "@codemirror/legacy-modes/mode/swift";
  import { lua } from "@codemirror/legacy-modes/mode/lua";
  import { r } from "@codemirror/legacy-modes/mode/r";
  import { toml } from "@codemirror/legacy-modes/mode/toml";
  import { dockerFile } from "@codemirror/legacy-modes/mode/dockerfile";
  import { powerShell } from "@codemirror/legacy-modes/mode/powershell";
  import { HighlightStyle, syntaxHighlighting } from "@codemirror/language";
  import { tags } from "@lezer/highlight";
  import { editorStore } from "$lib/stores/editor";
  import { configStore } from "$lib/stores/config";
  import EditorContextMenu from "./EditorContextMenu.svelte";
  import type { EditorTab } from "$lib/types/editor";
  import type { Extension } from "@codemirror/state";

  export let tab: EditorTab;

  let editorContainer: HTMLDivElement;
  let view: EditorView | null = null;
  let themeCompartment = new Compartment();

  // Context menu state
  let contextMenuShow = false;
  let contextMenuX = 0;
  let contextMenuY = 0;

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

  function closeContextMenu() {
    contextMenuShow = false;
  }

  // Subscribe to theme changes
  const config = configStore.config;

  // Light theme
  const lightTheme = EditorView.theme(
    {
      "&": {
        backgroundColor: "#ffffff",
        color: "#24292e",
      },
      ".cm-content": {
        caretColor: "#24292e",
      },
      ".cm-cursor, .cm-dropCursor": {
        borderLeftColor: "#24292e",
      },
      "&.cm-focused .cm-selectionBackground, .cm-selectionBackground, .cm-content ::selection": {
        backgroundColor: "#c8d3f5",
      },
      ".cm-panels": {
        backgroundColor: "#f6f8fa",
        color: "#24292e",
      },
      ".cm-panels.cm-panels-top": {
        borderBottom: "1px solid #e1e4e8",
      },
      ".cm-panels.cm-panels-bottom": {
        borderTop: "1px solid #e1e4e8",
      },
      ".cm-searchMatch": {
        backgroundColor: "#ffdf5d",
        outline: "1px solid #c4a000",
      },
      ".cm-searchMatch.cm-searchMatch-selected": {
        backgroundColor: "#c4a000",
      },
      ".cm-activeLine": {
        backgroundColor: "#f6f8fa",
      },
      ".cm-selectionMatch": {
        backgroundColor: "#c8d3f5",
      },
      ".cm-matchingBracket, .cm-nonmatchingBracket": {
        backgroundColor: "#c8d3f5",
        outline: "1px solid #888",
      },
      ".cm-gutters": {
        backgroundColor: "#f6f8fa",
        color: "#6a737d",
        border: "none",
        borderRight: "1px solid #e1e4e8",
      },
      ".cm-activeLineGutter": {
        backgroundColor: "#e1e4e8",
      },
      ".cm-foldPlaceholder": {
        backgroundColor: "transparent",
        border: "none",
        color: "#6a737d",
      },
      ".cm-tooltip": {
        border: "1px solid #e1e4e8",
        backgroundColor: "#ffffff",
      },
      ".cm-tooltip .cm-tooltip-arrow:before": {
        borderTopColor: "transparent",
        borderBottomColor: "transparent",
      },
      ".cm-tooltip .cm-tooltip-arrow:after": {
        borderTopColor: "#ffffff",
        borderBottomColor: "#ffffff",
      },
      ".cm-tooltip-autocomplete": {
        "& > ul > li[aria-selected]": {
          backgroundColor: "#e1e4e8",
          color: "#24292e",
        },
      },
    },
    { dark: false }
  );

  const lightHighlightStyle = HighlightStyle.define([
    { tag: tags.keyword, color: "#d73a49" },
    {
      tag: [tags.name, tags.deleted, tags.character, tags.propertyName, tags.macroName],
      color: "#6f42c1",
    },
    { tag: [tags.function(tags.variableName), tags.labelName], color: "#6f42c1" },
    { tag: [tags.color, tags.constant(tags.name), tags.standard(tags.name)], color: "#005cc5" },
    { tag: [tags.definition(tags.name), tags.separator], color: "#24292e" },
    {
      tag: [
        tags.typeName,
        tags.className,
        tags.number,
        tags.changed,
        tags.annotation,
        tags.modifier,
        tags.self,
        tags.namespace,
      ],
      color: "#e36209",
    },
    {
      tag: [
        tags.operator,
        tags.operatorKeyword,
        tags.url,
        tags.escape,
        tags.regexp,
        tags.link,
        tags.special(tags.string),
      ],
      color: "#032f62",
    },
    { tag: [tags.meta, tags.comment], color: "#6a737d" },
    { tag: tags.strong, fontWeight: "bold" },
    { tag: tags.emphasis, fontStyle: "italic" },
    { tag: tags.strikethrough, textDecoration: "line-through" },
    { tag: tags.link, color: "#032f62", textDecoration: "underline" },
    { tag: tags.heading, fontWeight: "bold", color: "#005cc5" },
    { tag: [tags.atom, tags.bool, tags.special(tags.variableName)], color: "#005cc5" },
    { tag: [tags.processingInstruction, tags.string, tags.inserted], color: "#22863a" },
    { tag: tags.invalid, color: "#cb2431" },
  ]);

  // High contrast theme
  const highContrastTheme = EditorView.theme(
    {
      "&": {
        backgroundColor: "#000000",
        color: "#ffffff",
      },
      ".cm-content": {
        caretColor: "#ffffff",
      },
      ".cm-cursor, .cm-dropCursor": {
        borderLeftColor: "#ffffff",
        borderLeftWidth: "2px",
      },
      "&.cm-focused .cm-selectionBackground, .cm-selectionBackground, .cm-content ::selection": {
        backgroundColor: "#264f78",
      },
      ".cm-panels": {
        backgroundColor: "#000000",
        color: "#ffffff",
      },
      ".cm-panels.cm-panels-top": {
        borderBottom: "2px solid #ffffff",
      },
      ".cm-panels.cm-panels-bottom": {
        borderTop: "2px solid #ffffff",
      },
      ".cm-searchMatch": {
        backgroundColor: "#515c6a",
        outline: "2px solid #ffff00",
      },
      ".cm-searchMatch.cm-searchMatch-selected": {
        backgroundColor: "#ffff00",
        color: "#000000",
      },
      ".cm-activeLine": {
        backgroundColor: "#1a1a1a",
      },
      ".cm-selectionMatch": {
        backgroundColor: "#264f78",
      },
      ".cm-matchingBracket, .cm-nonmatchingBracket": {
        backgroundColor: "#515c6a",
        outline: "2px solid #ffff00",
      },
      ".cm-gutters": {
        backgroundColor: "#000000",
        color: "#858585",
        border: "none",
        borderRight: "2px solid #ffffff",
      },
      ".cm-activeLineGutter": {
        backgroundColor: "#1a1a1a",
        color: "#ffffff",
      },
      ".cm-foldPlaceholder": {
        backgroundColor: "transparent",
        border: "none",
        color: "#ffff00",
      },
      ".cm-tooltip": {
        border: "2px solid #ffffff",
        backgroundColor: "#000000",
      },
      ".cm-tooltip .cm-tooltip-arrow:before": {
        borderTopColor: "transparent",
        borderBottomColor: "transparent",
      },
      ".cm-tooltip .cm-tooltip-arrow:after": {
        borderTopColor: "#000000",
        borderBottomColor: "#000000",
      },
      ".cm-tooltip-autocomplete": {
        "& > ul > li[aria-selected]": {
          backgroundColor: "#264f78",
          color: "#ffffff",
        },
      },
    },
    { dark: true }
  );

  const highContrastHighlightStyle = HighlightStyle.define([
    { tag: tags.keyword, color: "#569cd6", fontWeight: "bold" },
    {
      tag: [tags.name, tags.deleted, tags.character, tags.propertyName, tags.macroName],
      color: "#9cdcfe",
    },
    { tag: [tags.function(tags.variableName), tags.labelName], color: "#dcdcaa" },
    { tag: [tags.color, tags.constant(tags.name), tags.standard(tags.name)], color: "#4fc1ff" },
    { tag: [tags.definition(tags.name), tags.separator], color: "#ffffff" },
    {
      tag: [
        tags.typeName,
        tags.className,
        tags.number,
        tags.changed,
        tags.annotation,
        tags.modifier,
        tags.self,
        tags.namespace,
      ],
      color: "#4ec9b0",
    },
    {
      tag: [
        tags.operator,
        tags.operatorKeyword,
        tags.url,
        tags.escape,
        tags.regexp,
        tags.link,
        tags.special(tags.string),
      ],
      color: "#d4d4d4",
    },
    { tag: [tags.meta, tags.comment], color: "#6a9955" },
    { tag: tags.strong, fontWeight: "bold" },
    { tag: tags.emphasis, fontStyle: "italic" },
    { tag: tags.strikethrough, textDecoration: "line-through" },
    { tag: tags.link, color: "#3794ff", textDecoration: "underline" },
    { tag: tags.heading, fontWeight: "bold", color: "#569cd6" },
    { tag: [tags.atom, tags.bool, tags.special(tags.variableName)], color: "#569cd6" },
    { tag: [tags.processingInstruction, tags.string, tags.inserted], color: "#ce9178" },
    { tag: tags.invalid, color: "#f44747" },
  ]);

  function getThemeExtension(theme: string): Extension {
    switch (theme) {
      case "light":
        return [lightTheme, syntaxHighlighting(lightHighlightStyle)];
      case "high-contrast":
        return [highContrastTheme, syntaxHighlighting(highContrastHighlightStyle)];
      case "dark":
      case "custom":
      default:
        return oneDark;
    }
  }

  function getLanguageExtension(language: string): Extension {
    const languageMap: Record<string, () => Extension> = {
      javascript: () => javascript({ jsx: true, typescript: false }),
      typescript: () => javascript({ jsx: true, typescript: true }),
      python: () => python(),
      rust: () => rust(),
      html: () => html(),
      css: () => css(),
      json: () => json(),
      markdown: () => markdown(),
      xml: () => xml(),
      sql: () => sql(),
      java: () => java(),
      c: () => cpp(),
      cpp: () => cpp(),
      csharp: () => cpp(),
      php: () => php(),
      go: () => go(),
      yaml: () => yaml(),
      scss: () => sass(),
      sass: () => sass(),
      less: () => less(),
      vue: () => vue(),
      angular: () => angular(),
      wasm: () => wast(),
      shell: () => StreamLanguage.define(shell),
      ruby: () => StreamLanguage.define(ruby),
      swift: () => StreamLanguage.define(swift),
      lua: () => StreamLanguage.define(lua),
      r: () => StreamLanguage.define(r),
      toml: () => StreamLanguage.define(toml),
      dockerfile: () => StreamLanguage.define(dockerFile),
      powershell: () => StreamLanguage.define(powerShell),
      svelte: () => html(),
    };

    const getExtension = languageMap[language];
    return getExtension ? getExtension() : [];
  }

  function createEditor() {
    if (!editorContainer) return;

    const currentTheme = $config.theme;

    const saveKeymap = keymap.of([
      {
        key: "Mod-s",
        run: () => {
          editorStore.saveFile(tab.id);
          return true;
        },
      },
    ]);

    const updateListener = EditorView.updateListener.of((update) => {
      if (update.docChanged) {
        const content = update.state.doc.toString();
        editorStore.updateTabContent(tab.id, content);
      }
    });

    const state = EditorState.create({
      doc: tab.content,
      extensions: [
        basicSetup,
        themeCompartment.of(getThemeExtension(currentTheme)),
        getLanguageExtension(tab.language),
        saveKeymap,
        updateListener,
        EditorView.theme({
          "&": {
            height: "100%",
            fontSize: "14px",
          },
          ".cm-scroller": {
            overflow: "auto",
          },
          ".cm-content": {
            fontFamily: "'JetBrains Mono', 'Fira Code', 'Consolas', monospace",
          },
        }),
      ],
    });

    view = new EditorView({
      state,
      parent: editorContainer,
    });
  }

  function destroyEditor() {
    if (view) {
      view.destroy();
      view = null;
    }
  }

  // Watch for theme changes and update the editor
  $: if (view && $config.theme) {
    view.dispatch({
      effects: themeCompartment.reconfigure(getThemeExtension($config.theme)),
    });
  }

  onMount(() => {
    createEditor();
  });

  onDestroy(() => {
    destroyEditor();
  });

  $: if (view && tab.content !== view.state.doc.toString()) {
    view.dispatch({
      changes: {
        from: 0,
        to: view.state.doc.length,
        insert: tab.content,
      },
    });
  }
</script>

<!-- svelte-ignore a11y_no_static_element_interactions -->
<div class="code-editor" bind:this={editorContainer} oncontextmenu={handleContextMenu}></div>

{#if contextMenuShow && view}
  <EditorContextMenu
    x={contextMenuX}
    y={contextMenuY}
    editorView={view}
    onClose={closeContextMenu}
  />
{/if}

<style>
  .code-editor {
    flex: 1;
    overflow: hidden;
    background-color: var(--bg-terminal);
  }

  .code-editor :global(.cm-editor) {
    height: 100%;
  }

  .code-editor :global(.cm-focused) {
    outline: none;
  }
</style>
