'use client';

import { useRef, useEffect, useState, useCallback, forwardRef, useImperativeHandle } from 'react';
import type * as monaco from 'monaco-editor';

import { useMonaco } from '../hooks/useMonaco';
import { useEditorTheme } from '../hooks/useEditorTheme';
import type { EditorProps } from '../types';

export interface EditorRef {
  /** Get editor instance */
  getEditor: () => monaco.editor.IStandaloneCodeEditor | null;
  /** Get current value */
  getValue: () => string;
  /** Set value */
  setValue: (value: string) => void;
  /** Focus editor */
  focus: () => void;
}

/**
 * Monaco Editor Component
 *
 * A React wrapper around Monaco Editor with full TypeScript support.
 *
 * @example
 * ```tsx
 * <Editor
 *   value={code}
 *   language="typescript"
 *   onChange={(value) => setCode(value)}
 *   options={{ fontSize: 14, minimap: false }}
 * />
 * ```
 */
export const Editor = forwardRef<EditorRef, EditorProps>(function Editor(
  {
    value = '',
    language = 'plaintext',
    onChange,
    onMount,
    options = {},
    className = '',
    height = '100%',
    width = '100%',
    autoHeight = false,
    minHeight = 100,
    maxHeight = 600,
  },
  ref
) {
  const containerRef = useRef<HTMLDivElement>(null);
  const editorRef = useRef<monaco.editor.IStandaloneCodeEditor | null>(null);
  const { monaco, isLoading } = useMonaco();
  const resolvedTheme = useEditorTheme(monaco, options.theme);

  // Auto-height state
  const [contentHeight, setContentHeight] = useState<number | null>(null);

  const updateContentHeight = useCallback((editor: monaco.editor.IStandaloneCodeEditor) => {
    if (!autoHeight) return;
    const h = editor.getContentHeight();
    setContentHeight(Math.min(Math.max(h, minHeight), maxHeight));
  }, [autoHeight, minHeight, maxHeight]);

  // Track internal changes to prevent cursor reset on prop sync
  const isInternalChangeRef = useRef(false);

  // Expose editor methods via ref
  useImperativeHandle(ref, () => ({
    getEditor: () => editorRef.current,
    getValue: () => editorRef.current?.getValue() || '',
    setValue: (val: string) => editorRef.current?.setValue(val),
    focus: () => editorRef.current?.focus(),
  }));

  // Create editor
  useEffect(() => {
    if (!monaco || !containerRef.current || editorRef.current) return;

    const editor = monaco.editor.create(containerRef.current, {
      value,
      language,
      theme: resolvedTheme,
      fontSize: options.fontSize || 14,
      fontFamily: options.fontFamily || "'Fira Code', 'Consolas', monospace",
      tabSize: options.tabSize || 2,
      insertSpaces: options.insertSpaces !== false,
      wordWrap: options.wordWrap || 'on',
      minimap: { enabled: options.minimap !== false },
      lineNumbers: options.lineNumbers || 'on',
      readOnly: options.readOnly || false,
      automaticLayout: true,
      scrollBeyondLastLine: autoHeight ? false : false,
      scrollbar: autoHeight ? { vertical: 'hidden', horizontal: 'auto' } : undefined,
      overviewRulerLanes: autoHeight ? 0 : undefined,
      padding: { top: 16, bottom: 16 },
      renderLineHighlight: 'all',
      cursorBlinking: 'smooth',
      cursorSmoothCaretAnimation: 'on',
      smoothScrolling: true,
      bracketPairColorization: { enabled: true },
      guides: {
        bracketPairs: true,
        indentation: true,
      },
    });

    editorRef.current = editor;

    // Setup change listener
    if (onChange) {
      editor.onDidChangeModelContent(() => {
        // Mark as internal change to prevent cursor reset on prop sync
        isInternalChangeRef.current = true;
        onChange(editor.getValue());
      });
    }

    // Auto-height: resize container to fit content
    if (autoHeight) {
      editor.onDidContentSizeChange(() => updateContentHeight(editor));
      updateContentHeight(editor);
    }

    // Call onMount callback
    onMount?.(editor);

    return () => {
      editor.dispose();
      editorRef.current = null;
    };
  }, [monaco]);

  // Update value when prop changes (only for external changes)
  useEffect(() => {
    const editor = editorRef.current;
    if (!editor) return;

    // Skip if this is an internal change (user typing) - prevents cursor reset
    if (isInternalChangeRef.current) {
      isInternalChangeRef.current = false;
      return;
    }

    const currentValue = editor.getValue();
    if (value !== currentValue) {
      // Save cursor position
      const position = editor.getPosition();
      const selections = editor.getSelections();

      editor.setValue(value);

      // Restore cursor position if possible
      if (position) {
        editor.setPosition(position);
      }
      if (selections && selections.length > 0) {
        editor.setSelections(selections);
      }
    }
  }, [value]);

  // Update language when prop changes
  useEffect(() => {
    const editor = editorRef.current;
    if (!editor || !monaco) return;

    const model = editor.getModel();
    if (model) {
      monaco.editor.setModelLanguage(model, language);
    }
  }, [language, monaco]);

  // Update options when props change
  useEffect(() => {
    const editor = editorRef.current;
    if (!editor) return;

    editor.updateOptions({
      theme: resolvedTheme,
      fontSize: options.fontSize,
      readOnly: options.readOnly,
      minimap: { enabled: options.minimap !== false },
      wordWrap: options.wordWrap,
      lineNumbers: options.lineNumbers,
    });
  }, [options, resolvedTheme]);

  if (isLoading) {
    return (
      <div
        className={className}
        style={{
          width,
          height,
          display: 'flex',
          alignItems: 'center',
          justifyContent: 'center',
          backgroundColor: '#1e1e1e',
          color: '#666',
        }}
      >
        Loading editor...
      </div>
    );
  }

  const resolvedHeight = autoHeight && contentHeight != null ? contentHeight : height;

  return (
    <div
      ref={containerRef}
      className={className}
      style={{
        width,
        height: resolvedHeight,
        ...(autoHeight && { minHeight: minHeight, maxHeight: maxHeight, overflow: 'hidden' }),
      }}
    />
  );
});
