'use client';

import { useEffect, useRef, useState } from 'react';
import dynamic from 'next/dynamic';
import { useTheme } from 'next-themes';

import { Button, Switch } from '@spoon/ui';

import type { MonacoLike } from './monaco-theme';
import { languageForPath } from './languages';
import {
  configureSpoonMonaco,
  remeasureFontsWhenReady,
  SPOON_DARK,
  SPOON_LIGHT,
} from './monaco-theme';

const MonacoEditor = dynamic(async () => await import('@monaco-editor/react'), {
  ssr: false,
});

const EDITOR_FONT_FAMILY =
  "var(--font-victor-mono), 'Symbols Nerd Font Mono', 'Geist Mono', ui-monospace, SFMono-Regular, monospace";

type MonacoEditorInstance = {
  getModel?: () => unknown;
  dispose?: () => void;
};

type VimMode = {
  dispose: () => void;
};

export const CodeEditor = ({
  path,
  content,
  savedContent,
  readOnly,
  vimEnabled,
  conflicted,
  onSave,
  onChange,
  onVimEnabledChange,
}: {
  path?: string;
  content: string;
  savedContent: string;
  readOnly: boolean;
  vimEnabled: boolean;
  conflicted?: boolean;
  onSave: (content: string) => Promise<void>;
  onChange: (content: string) => void;
  onVimEnabledChange: (enabled: boolean) => void;
}) => {
  const [saving, setSaving] = useState(false);
  const editorRef = useRef<MonacoEditorInstance | null>(null);
  const vimRef = useRef<VimMode | null>(null);
  const statusRef = useRef<HTMLDivElement | null>(null);
  const { resolvedTheme } = useTheme();
  const editorTheme = resolvedTheme === 'light' ? SPOON_LIGHT : SPOON_DARK;

  // Dispose the Monaco instance and clear the ref on unmount so a remount
  // (e.g. the Phase-1 key={jobId} swap) doesn't leave a stale editor/vim
  // binding leaked behind the new mount.
  useEffect(() => {
    return () => {
      vimRef.current?.dispose();
      vimRef.current = null;
      editorRef.current?.dispose?.();
      editorRef.current = null;
    };
  }, []);

  useEffect(() => {
    const editor = editorRef.current;
    if (!editor) return;
    vimRef.current?.dispose();
    vimRef.current = null;
    if (!vimEnabled) return;
    void import('monaco-vim').then((module) => {
      const initVimMode = module.initVimMode as unknown as (
        editor: MonacoEditorInstance,
        statusNode?: HTMLElement | null,
      ) => VimMode;
      vimRef.current = initVimMode(editor, statusRef.current);
    });
    return () => {
      vimRef.current?.dispose();
      vimRef.current = null;
    };
  }, [vimEnabled, path]);

  if (!path) {
    return (
      <div className='text-muted-foreground flex h-full items-center justify-center text-sm'>
        Select a file to inspect or edit.
      </div>
    );
  }

  const save = async () => {
    setSaving(true);
    try {
      await onSave(content);
    } finally {
      setSaving(false);
    }
  };

  const dirty = content !== savedContent;

  return (
    <div className='flex h-full min-h-0 flex-col'>
      <div className='border-border flex h-14 items-center justify-between gap-3 border-b px-3'>
        <div className='min-w-0'>
          <p className='text-muted-foreground text-[11px] font-medium tracking-wide uppercase'>
            Editor
          </p>
          <p className='truncate font-mono text-xs'>{path}</p>
          {conflicted ? (
            <p className='text-amber-500 text-xs'>
              This file changed on disk — your unsaved edits may conflict.
            </p>
          ) : dirty ? (
            <p className='text-muted-foreground text-xs'>Unsaved changes</p>
          ) : null}
        </div>
        <div className='flex items-center gap-3'>
          <label className='flex items-center gap-2 text-xs'>
            Vim
            <Switch checked={vimEnabled} onCheckedChange={onVimEnabledChange} />
          </label>
          <Button
            type='button'
            size='sm'
            disabled={readOnly || saving || !dirty}
            onClick={save}
          >
            {saving ? 'Saving...' : 'Save'}
          </Button>
        </div>
      </div>
      <div className='min-h-0 flex-1'>
        <MonacoEditor
          height='100%'
          width='100%'
          path={path}
          language={languageForPath(path)}
          value={content}
          theme={editorTheme}
          beforeMount={(monaco) => {
            configureSpoonMonaco(monaco as unknown as MonacoLike);
          }}
          options={{
            readOnly,
            minimap: { enabled: false },
            fontFamily: EDITOR_FONT_FAMILY,
            fontLigatures: true,
            fontSize: 13,
            lineHeight: 1.6,
            scrollBeyondLastLine: false,
            wordWrap: 'on',
            automaticLayout: true,
            smoothScrolling: true,
            cursorSmoothCaretAnimation: 'on',
            padding: { top: 12, bottom: 12 },
            scrollbar: { alwaysConsumeMouseWheel: false },
            quickSuggestions: true,
            suggestOnTriggerCharacters: true,
            tabCompletion: 'on',
            wordBasedSuggestions: 'matchingDocuments',
            bracketPairColorization: { enabled: true },
            renderWhitespace: 'selection',
          }}
          onMount={(editor, monaco) => {
            editorRef.current = editor as MonacoEditorInstance;
            remeasureFontsWhenReady(monaco as unknown as MonacoLike);
          }}
          onChange={(next) => {
            const nextValue = next ?? '';
            onChange(nextValue);
          }}
        />
      </div>
      <div
        ref={statusRef}
        className='border-border text-muted-foreground h-6 border-t px-3 py-1 font-mono text-xs'
      />
    </div>
  );
};
