<script setup lang="ts">
import * as monaco from 'monaco-editor';
import editorWorker from 'monaco-editor/esm/vs/editor/editor.worker?worker';
import tsWorker from 'monaco-editor/esm/vs/language/typescript/ts.worker?worker';
import {onBeforeUnmount, onMounted, ref, watch} from 'vue';
import {fetchScriptDefinitions} from './rules';

(self as unknown as { MonacoEnvironment: { getWorker(workerId: string, label: string): Worker } }).MonacoEnvironment = {
  getWorker(_workerId: string, label: string) {
    if (label === 'typescript' || label === 'javascript') {
      return new tsWorker();
    }
    return new editorWorker();
  }
};

let extraLibLoaded = false;

const loadScriptDefinitions = async () => {
  if (extraLibLoaded) {
    return;
  }
  try {
    const definitions = await fetchScriptDefinitions();
    monaco.typescript.javascriptDefaults.addExtraLib(definitions, 'ts:rule-runner.d.ts');
    extraLibLoaded = true;
  } catch {
    // type definitions are optional — the editor still works without autocomplete
  }
};

const props = defineProps<{ ledgerRefs?: string[] }>();

let ledgersLib: monaco.IDisposable | undefined;

const updateLedgerRefs = (refs: readonly string[]) => {
  ledgersLib?.dispose();
  const members = refs
      .filter(ref => ref && ref !== 'system')
      .map(ref => `    readonly ${JSON.stringify(ref)}: Ledger;`)
      .join('\n');
  ledgersLib = monaco.typescript.javascriptDefaults.addExtraLib(
      `declare interface Ledgers {\n${members}\n}\n`,
      'ts:rule-runner.ledgers.d.ts'
  );
};

const model = defineModel<string>({default: ''});
const container = ref<HTMLElement>();
let editor: monaco.editor.IStandaloneCodeEditor | undefined;

onMounted(async () => {
  await loadScriptDefinitions();
  updateLedgerRefs(props.ledgerRefs ?? []);

  if (!container.value) {
    return;
  }

  editor = monaco.editor.create(container.value, {
    value: model.value,
    language: 'javascript',
    theme: 'vs-dark',
    automaticLayout: true,
    minimap: {enabled: false},
    scrollBeyondLastLine: false,
    fontSize: 13,
    tabSize: 2,
  });

  editor.onDidChangeModelContent(() => {
    const value = editor!.getValue();

    if (value !== model.value) {
      model.value = value;
    }
  });
});

watch(model, value => {
  if (editor && value !== editor.getValue()) {
    editor.setValue(value ?? '');
  }
});

watch(() => props.ledgerRefs, refs => updateLedgerRefs(refs ?? []), {deep: true});

onBeforeUnmount(() => {
  editor?.dispose();
  ledgersLib?.dispose();
});
</script>

<template>
  <div ref="container" class="script-editor"></div>
</template>

<style scoped>
.script-editor {
  width: 100%;
  flex: 1 1 auto;
  min-height: 12rem;
}
</style>