import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, OnInit, ViewChild } from '@angular/core';
import { PermissionsService } from '@services/permissions.service';
import { IconButtonTypes, LoadingService, Toaster } from '@iqser/common-ui';
import { saveAs } from 'file-saver';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { RulesService } from '../../../services/rules.service';
import { firstValueFrom } from 'rxjs';
import { DOSSIER_TEMPLATE_ID } from '@red/domain';
import { EditorThemeService } from '@services/editor-theme.service';
import { ComponentCanDeactivate } from '@guards/can-deactivate.guard';
import { Debounce, getParam } from '@iqser/common-ui/lib/utils';
import ICodeEditor = monaco.editor.ICodeEditor;
import IModelDeltaDecoration = monaco.editor.IModelDeltaDecoration;
import IStandaloneEditorConstructionOptions = monaco.editor.IStandaloneEditorConstructionOptions;

@Component({
    templateUrl: './rules-screen.component.html',
    styleUrls: ['./rules-screen.component.scss'],
    changeDetection: ChangeDetectionStrategy.OnPush,
})
export class RulesScreenComponent implements OnInit, ComponentCanDeactivate {
    @ViewChild('fileInput')
    private _fileInput: ElementRef;
    private _codeEditor: ICodeEditor;
    private _decorations: string[] = [];
    readonly #dossierTemplateId = getParam(DOSSIER_TEMPLATE_ID);
    readonly iconButtonTypes = IconButtonTypes;
    readonly editorOptions: IStandaloneEditorConstructionOptions = {
        theme: 'vs',
        language: 'java',
        automaticLayout: true,
        readOnly: !this.permissionsService.canEditRules(),
    };
    initialLines: string[] = [];
    currentLines: string[] = [];
    isLeaving = false;

    constructor(
        readonly permissionsService: PermissionsService,
        private readonly _rulesService: RulesService,
        private readonly _changeDetectorRef: ChangeDetectorRef,
        private readonly _toaster: Toaster,
        private readonly _loadingService: LoadingService,
        private readonly _editorThemeService: EditorThemeService,
    ) {}

    set isLeavingPage(isLeaving: boolean) {
        this.isLeaving = isLeaving;
        this._changeDetectorRef.detectChanges();
    }

    get changed(): boolean {
        return this.currentLines.toString() !== this.initialLines.toString();
    }

    get codeEditorText() {
        return this.currentLines.join('\n');
    }

    set codeEditorText($event: any) {
        this.currentLines = $event.split('\n');
        this.codeEditorTextChanged();
    }

    async ngOnInit() {
        await this._initialize();
    }

    onCodeEditorInit(editor: ICodeEditor) {
        this._codeEditor = editor;
        for (const theme of this._editorThemeService.themes) {
            (window as any).monaco.editor.defineTheme(theme, this._editorThemeService.configurations[theme]);
        }
        (window as any).monaco.editor.setTheme(this._editorThemeService.getTheme(true));
        this._changeDetectorRef.detectChanges();
    }

    @Debounce()
    codeEditorTextChanged() {
        const newDecorations = this.currentLines.filter(entry => this._isNew(entry)).map(entry => this._makeDecorationFor(entry));

        this._decorations = this._codeEditor.deltaDecorations(this._decorations, newDecorations);
    }

    async save(): Promise<void> {
        this._loadingService.start();
        await firstValueFrom(
            this._rulesService.uploadRules({
                rules: this._codeEditor.getModel().getValue(),
                dossierTemplateId: this.#dossierTemplateId,
            }),
        ).then(
            async () => {
                await this._initialize();
                this._toaster.success(_('rules-screen.success.generic'));
            },
            () => {
                this._loadingService.stop();
                this._toaster.error(_('rules-screen.error.generic'));
            },
        );
    }

    revert(): void {
        this.currentLines = this.initialLines;
        this._decorations = this._codeEditor?.deltaDecorations(this._decorations, []) || [];
        this._changeDetectorRef.detectChanges();
        this._loadingService.stop();
    }

    download(): void {
        const content = this._codeEditor.getModel().getValue();
        const blob = new Blob([content], {
            type: 'text/plain;charset=utf-8',
        });
        saveAs(blob, 'rules.txt');
    }

    upload($event): void {
        const file: File = $event.target.files[0];
        const fileReader = new FileReader();

        if (file) {
            fileReader.onload = () => {
                this._codeEditor.getModel().setValue(fileReader.result as string);
                this._fileInput.nativeElement.value = null;
            };
            fileReader.readAsText(file);
        }
    }

    private _isNew(entry: string): boolean {
        return this.initialLines.indexOf(entry) < 0 && entry?.trim().length > 0;
    }

    private _makeDecorationFor(entry: string): IModelDeltaDecoration {
        const line = this.currentLines.indexOf(entry) + 1;

        return {
            range: new monaco.Range(line, 1, line, 1),
            options: { isWholeLine: true, className: 'changed-row-marker' },
        } as IModelDeltaDecoration;
    }

    private async _initialize() {
        this._loadingService.start();
        await firstValueFrom(this._rulesService.download(this.#dossierTemplateId)).then(
            rules => {
                this.currentLines = this.initialLines = rules.rules.split('\n');
                this.revert();
            },
            () => this._loadingService.stop(),
        );
    }
}
