<template>
  <div class="flex h-full min-h-0 flex-col">
    <!-- barre d'outils de l'éditeur -->
    <div class="flex items-center gap-2 border-b border-zinc-800 px-3 py-1 text-xs">
      <span class="min-w-0 flex-1 truncate font-mono text-zinc-400" :title="file">{{ file }}</span>
      <span v-if="dirty" class="text-amber-400" :title="t('editor.unsaved')">●</span>
      <BaseButton size="sm" variant="ghost" :icon="Save" :loading="saving" :disabled="!dirty || !ready" @click="save">
        {{ t('editor.save') }}
      </BaseButton>
    </div>

    <!-- bannière de conflit (le fichier a changé sur le disque depuis le chargement) -->
    <div v-if="conflict" class="flex flex-wrap items-center gap-2 border-b border-amber-700/40 bg-amber-950/30 px-3 py-1.5 text-xs text-amber-200">
      <TriangleAlert :size="14" /> {{ t('editor.conflict') }}
      <div class="ml-auto flex items-center gap-2">
        <BaseButton size="sm" variant="ghost" @click="reload">{{ t('editor.reload') }}</BaseButton>
        <BaseButton size="sm" variant="danger" :loading="saving" @click="overwrite">{{ t('editor.overwrite') }}</BaseButton>
      </div>
    </div>

    <p v-if="loadError" class="px-3 py-2 text-xs text-rose-400">{{ loadError }}</p>
    <!-- conteneur Monaco -->
    <div ref="host" class="min-h-0 flex-1" />
  </div>
</template>

<script setup lang="ts">
import { onBeforeUnmount, onMounted, ref, shallowRef, watch } from 'vue';
import { useI18n } from 'vue-i18n';
import { Save, TriangleAlert } from '@lucide/vue';
import type * as Monaco from 'monaco-editor';
import { gitApi } from '../../lib/git-api';
import { ApiError } from '../../lib/api';
import { loadMonaco } from '../../composables/useMonaco';
import BaseButton from '../ui/BaseButton.vue';

const props = defineProps<{ repoId: string; wt: string; file: string }>();
const emit = defineEmits<{ saved: [] }>();
const { t } = useI18n();

const host = ref<HTMLElement | null>(null);
const ready = ref(false);
const dirty = ref(false);
const saving = ref(false);
const conflict = ref(false);
const loadError = ref<string | null>(null);

let monaco: typeof Monaco | null = null;
const editor = shallowRef<Monaco.editor.IStandaloneCodeEditor | null>(null);
let model: Monaco.editor.ITextModel | null = null;
let baseMtime: number | undefined;
let savedContent = '';
let disposed = false;

onMounted(async () => {
  monaco = await loadMonaco();
  if (disposed || !host.value) return;
  editor.value = monaco.editor.create(host.value, {
    value: '',
    theme: 'arboretum-dark',
    automaticLayout: true,
    minimap: { enabled: false },
    wordWrap: 'on',
    fontSize: 13,
    scrollBeyondLastLine: false,
    tabSize: 2,
  });
  editor.value.onDidChangeModelContent(() => {
    dirty.value = editor.value?.getValue() !== savedContent;
  });
  // Ctrl/Cmd+S : sauvegarde. Monaco n'intercepte que si l'éditeur a le focus → pas de capture globale.
  editor.value.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyCode.KeyS, () => void save());
  ready.value = true;
  await loadFile();
});

async function loadFile(): Promise<void> {
  if (!monaco || !editor.value) return;
  loadError.value = null;
  conflict.value = false;
  try {
    const res = await gitApi.readFile(props.repoId, props.wt, props.file);
    if (disposed) return;
    baseMtime = res.mtime;
    savedContent = res.content;
    const next = monaco.editor.createModel(res.content, res.language);
    editor.value.setModel(next);
    model?.dispose();
    model = next;
    dirty.value = false;
  } catch (err) {
    loadError.value = err instanceof Error ? err.message : String(err);
  }
}

async function save(): Promise<void> {
  if (!editor.value || saving.value || !dirty.value) return;
  saving.value = true;
  conflict.value = false;
  try {
    const content = editor.value.getValue();
    const res = await gitApi.writeFile(props.repoId, props.wt, props.file, content, baseMtime);
    baseMtime = res.mtime;
    savedContent = content;
    dirty.value = false;
    emit('saved');
  } catch (err) {
    if (err instanceof ApiError && err.code === 'STALE_FILE') conflict.value = true;
    else loadError.value = err instanceof Error ? err.message : String(err);
  } finally {
    saving.value = false;
  }
}

// Écrase malgré le conflit (PUT sans baseMtime → le serveur ne vérifie plus la fraîcheur).
async function overwrite(): Promise<void> {
  if (!editor.value || saving.value) return;
  saving.value = true;
  try {
    const content = editor.value.getValue();
    const res = await gitApi.writeFile(props.repoId, props.wt, props.file, content);
    baseMtime = res.mtime;
    savedContent = content;
    dirty.value = false;
    conflict.value = false;
    emit('saved');
  } catch (err) {
    loadError.value = err instanceof Error ? err.message : String(err);
  } finally {
    saving.value = false;
  }
}

// Recharge depuis le disque (abandonne les modifications locales).
async function reload(): Promise<void> {
  await loadFile();
}

watch(() => [props.repoId, props.wt, props.file], () => void loadFile());

onBeforeUnmount(() => {
  disposed = true;
  model?.dispose();
  editor.value?.dispose();
});
</script>
