import { Component, ViewChild } from '@angular/core';
import { CodeExecutionService } from 'src/app/services/codeExecution.service';
import { Compartment } from '@codemirror/state';
import { CodeMirrorComponent } from '@sandkasten/codemirror6-editor';
import { LanguageDescription } from '@codemirror/language';
import { CODE_DEFAULTS, LANGUAGES } from '../languages';
import { SafeHTMLPipe } from '../../safe-html.pipe';
import { ReactiveFormsModule, FormsModule } from '@angular/forms';
import {
  keymap,
  highlightSpecialChars,
  drawSelection,
  highlightActiveLine,
  dropCursor,
  rectangularSelection,
  crosshairCursor,
  lineNumbers,
  highlightActiveLineGutter,
  gutter,
} from '@codemirror/view';
import { Extension, EditorState } from '@codemirror/state';
import {
  defaultHighlightStyle,
  syntaxHighlighting,
  indentOnInput,
  bracketMatching,
  foldGutter,
  foldKeymap,
} from '@codemirror/language';
import { defaultKeymap, history, historyKeymap } from '@codemirror/commands';
import { searchKeymap, highlightSelectionMatches } from '@codemirror/search';
import {
  autocompletion,
  completionKeymap,
  closeBrackets,
  closeBracketsKeymap,
} from '@codemirror/autocomplete';
import { lintKeymap } from '@codemirror/lint';

const basicSetup: Extension = (() => [
  highlightActiveLineGutter(),
  highlightSpecialChars(),
  history(),
  foldGutter(),
  drawSelection(),
  dropCursor(),
  EditorState.allowMultipleSelections.of(true),
  indentOnInput(),
  syntaxHighlighting(defaultHighlightStyle, { fallback: true }),
  bracketMatching(),
  closeBrackets(),
  autocompletion(),
  rectangularSelection(),
  crosshairCursor(),
  highlightActiveLine(),
  highlightSelectionMatches(),
  keymap.of([
    ...closeBracketsKeymap,
    ...defaultKeymap,
    ...searchKeymap,
    ...historyKeymap,
    ...foldKeymap,
    ...completionKeymap,
    ...lintKeymap,
  ]),
])();

@Component({
  selector: 'app-editor',
  templateUrl: './editor.component.html',
  styleUrls: ['./editor.component.scss'],
  standalone: true,
  imports: [
    CodeMirrorComponent,
    ReactiveFormsModule,
    FormsModule,
    SafeHTMLPipe,
  ],
})
export class EditorComponent {
  isLoaded: boolean = false; // Pour vérifier si le chargement est terminé

  readonly languages: LanguageDescription[] = LANGUAGES;
  // Mode par défaut
  private _selectedLanguage = this.languages.find(
    (lang) => lang.name === 'JavaScript'
  )!;
  get selectedLanguage(): LanguageDescription {
    return this._selectedLanguage;
  }
  set selectedLanguage(value: LanguageDescription) {
    this._selectedLanguage = value;
    if (value.name in CODE_DEFAULTS) {
      this.editorContent =
        CODE_DEFAULTS[value.name as keyof typeof CODE_DEFAULTS];
    }
    this.selectedLanguage.load().then((language) => {
      this.codemirror.editor?.dispatch({
        effects: this.languageCompartment.reconfigure(language),
      });
    });
  }
  private _linesNumbers: boolean = true;
  get linesNumbers() {
    return this._linesNumbers;
  }
  set linesNumbers(lines: boolean) {
    this._linesNumbers = lines;
    this.codemirror.editor?.dispatch({
      effects: this.gutterCompartment.reconfigure(
        lines ? lineNumbers() : gutter({})
      ),
    });
  }

  // Contenu de l'éditeur que l'on passera au serveur
  editorContent: string =
    CODE_DEFAULTS[this.selectedLanguage.name as keyof typeof CODE_DEFAULTS];
  resultContent: string = '';

  // Message d'erreur
  errorMessage: string = '';

  @ViewChild(CodeMirrorComponent) private codemirror!: CodeMirrorComponent;

  private readonly languageCompartment = new Compartment();
  private readonly gutterCompartment = new Compartment();
  protected readonly extensions: Extension[] = [
    basicSetup,
    this.gutterCompartment.of(lineNumbers()),
    this.languageCompartment.of(this.selectedLanguage.support!),
  ];

  constructor(private codeExecutionService: CodeExecutionService) {}

  // Efface le contenu de l'éditeur
  clear(): void {
    this.editorContent = '';
  }

  onRunButtonClicked() {
    // Le code à exécuter est le contenu de l'éditeur
    const codeToExecute = this.editorContent;

    this.codeExecutionService.executeCode(
      codeToExecute,
      this.selectedLanguage.name
    );

    this.resultContent = '';
  }

  loadFromFile(event: Event) {
    const file = (event.target as HTMLInputElement).files![0];
    for (const language of this.languages) {
      if (language.extensions.some((ext) => file.name.endsWith(`.${ext}`))) {
        this.selectedLanguage = language;
        const reader = new FileReader();
        reader.onload = (event) => {
          this.editorContent = event.target!.result as string;
          this.errorMessage = '';
        };
        reader.readAsText(file);
        return;
      }
    }
    const extensions = this.languages.flatMap((lang) => lang.extensions);
    this.errorMessage = `Unsupported language. Please select one of the following languages: ${extensions.join(', ')}.`;
    console.error(this.errorMessage);
  }

  saveToFile() {
    const blob = new Blob([this.editorContent], { type: 'text/plain' });
    const a = document.createElement('a');
    a.download = `code.${this.selectedLanguage.extensions![0]}`;
    a.href = URL.createObjectURL(blob);
    a.click();
  }
}