import {
  Component,
  ElementRef,
  forwardRef,
  Input,
  OnInit,
  ViewChild,
  OnDestroy,
} from '@angular/core';
import { EditorView, basicSetup } from 'codemirror';
import { FormControl } from '@angular/forms';
import { EditorState } from '@codemirror/state';
import { java } from '@codemirror/lang-java';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';

@Component({
  standalone: true,
  selector: 'code-mirror-field',
  template: `<div #editorContainer style="border: 1px solid #ccc; max-height: 300px; overflow: auto; resize: vertical;"></div>`,
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef(() => CodeMirrorFieldComponent),
      multi: true
    }
  ]
})
export class CodeMirrorFieldComponent implements OnInit, OnDestroy, ControlValueAccessor {
  @ViewChild('editorContainer', { static: true }) editorContainer!: ElementRef;
  
  private editor!: EditorView;
  private readonly: boolean = false;
  
  value: any = '';
  onChange: (value: any) => void = () => {};
  onTouched: () => void = () => {};
  
  private cursorListener = EditorView.updateListener.of((update) => {
    if (update.selectionSet) {
      const cursorPosition = update.state.selection.main.head;
      const word = this.getCurrentWord(update.state, cursorPosition);
      console.log('Current word:', word);
    }
  });
  
  private updateListener = EditorView.updateListener.of((update) => {
    if (update.docChanged) {
      const content = update.state.doc.toString();
      this.value = content;
      this.onChange(this.value);  // Fire ControlValueAccessor change event
      this.onTouched();           // Fire ControlValueAccessor touched event
    }
  });
  
  /* Interface OnInit */
  ngOnInit(): void {
    this.initializeEditor();
  }

  /* Interface OnDestroy */
  ngOnDestroy(): void {
    // Destruir o editor ao sair do componente
    this.editor.destroy();
  }
  
  /* Interface ControlValueAccessor */
  writeValue(value: any): void {
    this.value = value;
    this.updateEditorContent(value);
  }

  registerOnChange(fn: any): void {
    this.onChange = fn;
  }

  registerOnTouched(fn: any): void {
    this.onTouched = fn;
  }
  
  setDisabledState(isDisabled: boolean): void {
    this.readonly = isDisabled;
    this.editor.setState(this.createEditorState());
  }
  
  private initializeEditor(): void {
    /*
    this.editor = new EditorView({
      state: EditorState.create({
        doc: '', // Initialize empty. The ControlValueAccessor.writeValue will be called and set the value
        extensions: [basicSetup, java(), this.updateListener, this.cursorListener, EditorView.editable.of(this.readonly)],
      }),
      parent: this.editorContainer.nativeElement,
    });
    */
    this.editor = new EditorView({
      state: this.createEditorState(),
      parent: this.editorContainer.nativeElement,
    });
  }

  private createEditorState(): EditorState{
    let content = '';
    if (this.editor) {
      content = this.editor.state.doc.toString();   // Preserve the current document
    }
    
    const newState = EditorState.create({
      doc: content, 
      extensions: [
        basicSetup, 
        java(), 
        this.updateListener, 
        this.cursorListener,
        EditorView.editable.of(!this.readonly), // Set readonly mode
      ],
    });
    
    return newState;
  }

  private updateEditorContent(value: string): void {
    this.editor.dispatch({
      changes: { from: 0, to: this.editor.state.doc.length, insert: value },
    });
  }
  
  /**
   * Get the current word based on cursor position
   */
  private getCurrentWord(state: EditorState, position: number): string | null {
    const line = state.doc.lineAt(position); // Get the current line
    const lineText = line.text;
    const cursorIndex = position - line.from; // Cursor position within the line

    // Find the word around the cursor
    const left = lineText.slice(0, cursorIndex).match(/\b\w+$/);
    const right = lineText.slice(cursorIndex).match(/^\w+\b/);

    const leftWord = left ? left[0] : '';
    const rightWord = right ? right[0] : '';

    return leftWord + rightWord; // Combine both parts to get the full word
  }
}
