import { CdkDrag, CdkDragDrop, CdkDragHandle, CdkDropList, moveItemInArray } from '@angular/cdk/drag-drop';
import { AsyncPipe, KeyValuePipe, NgClass, NgForOf, NgIf } from '@angular/common';
import { Component, computed, input, OnInit, output, signal, WritableSignal } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { MatIcon } from '@angular/material/icon';
import { CircleButtonComponent, HelpModeService, IconButtonComponent, IconButtonTypes, IqserDialog } from '@iqser/common-ui';
import { TranslateModule } from '@ngx-translate/core';
import { IComponentLogEntry, IComponentValue } from '@red/domain';
import { RevertValueDialogComponent } from '../../dialogs/docu-mine/revert-value-dialog/revert-value-dialog.component';
import { FilePreviewStateService } from '../../services/file-preview-state.service';
import { escapeHtml } from '@common-ui/utils';
import { ReplaceNbspPipe } from '@common-ui/pipes/replace-nbsp.pipe';
import { MatTooltip } from '@angular/material/tooltip';

@Component({
    selector: 'redaction-editable-structured-component-value [entry] [canEdit]',
    templateUrl: './editable-structured-component-value.component.html',
    styleUrls: ['./editable-structured-component-value.component.scss'],
    standalone: true,
    imports: [
        CircleButtonComponent,
        NgClass,
        TranslateModule,
        KeyValuePipe,
        CdkDropList,
        MatIcon,
        IconButtonComponent,
        CdkDrag,
        NgIf,
        NgForOf,
        CdkDragHandle,
        FormsModule,
        AsyncPipe,
        ReplaceNbspPipe,
        MatTooltip,
    ],
})
export class EditableStructuredComponentValueComponent implements OnInit {
    readonly entry = input<IComponentLogEntry>();
    currentEntry: WritableSignal<IComponentLogEntry>;
    readonly canEdit = input<boolean>();
    readonly deselectLast = output();
    readonly overrideValue = output<IComponentLogEntry>();
    readonly revertOverride = output<string>();
    hasUpdatedValues = computed(() => this.currentEntry().overridden);
    selected = false;
    valueBeforeCurrentEdit: IComponentValue[];
    protected entryLabel: string;
    protected editing = false;
    protected initialEntry: IComponentLogEntry;
    protected readonly iconButtonTypes = IconButtonTypes;

    constructor(
        readonly helpModeService: HelpModeService,
        private readonly _iqserDialog: IqserDialog,
        private readonly _state: FilePreviewStateService,
    ) {}

    get disabled() {
        for (let i = 0; i < this.currentEntry().componentValues.length; i++) {
            if (this.currentEntry().componentValues[i].value !== this.initialEntry.componentValues[i]?.value) {
                return false;
            }
        }
        return this.currentEntry().componentValues.length === this.initialEntry.componentValues.length;
    }

    get #initialEntry() {
        return JSON.parse(JSON.stringify(this.entry()));
    }

    ngOnInit() {
        this.currentEntry = signal(this.entry());
        this.reset();
    }

    reset() {
        this.initialEntry = this.#initialEntry;
        this.entryLabel = this.parseName(this.currentEntry().name);
        this.deselect();
    }

    select() {
        if (!this.editing) {
            if (this.selected) {
                this.deselect();
                return;
            }
            this.deselectLast.emit();
            this.selected = true;
            this._state.componentReferenceIds = this.#getUniqueReferencesIds(this.currentEntry().componentValues);
        }
    }

    edit() {
        this.valueBeforeCurrentEdit = JSON.parse(JSON.stringify([...this.currentEntry().componentValues]));
        this.deselectLast.emit();
        this.selected = true;
        this.editing = true;
        this.#updateTextAreaHeight();
    }

    deselect($event?: MouseEvent) {
        $event?.stopImmediatePropagation();
        this.selected = false;
        this.editing = false;
        this._state.componentReferenceIds = null;
    }

    cancel($event?: MouseEvent) {
        this.currentEntry.update(value => ({ ...value, componentValues: this.valueBeforeCurrentEdit }));
        this.deselect($event);
    }

    removeValue(index: number) {
        this.currentEntry.update(value => ({ ...value, componentValues: value.componentValues.filter((_, i) => i !== index) }));
    }

    save() {
        this.currentEntry.update(value => ({ ...value, overridden: true }));
        this.overrideValue.emit(this.currentEntry());
        this.reset();
    }

    async undo() {
        const dialog = this._iqserDialog.openDefault(RevertValueDialogComponent, { data: { entry: this.currentEntry() }, width: '800px' });
        const result = await dialog.result();
        if (result) {
            this.revertOverride.emit(this.currentEntry().name);
            this.reset();
        }
    }

    add() {
        this.currentEntry.update(value => ({
            ...value,
            componentValues: [
                ...value.componentValues,
                {
                    componentRuleId: null,
                    entityReferences: [],
                    originalValue: null,
                    value: '',
                    valueDescription: '',
                },
            ],
        }));
    }

    drop(event: CdkDragDrop<string>) {
        moveItemInArray(this.currentEntry().componentValues, event.previousIndex, event.currentIndex);
    }

    parseName(name: string) {
        return name.replaceAll('_', ' ');
    }

    transformNewLines(value: string) {
        return escapeHtml(value).replace(/\n/g, '<br>');
    }

    #getUniqueReferencesIds(values: IComponentValue[]) {
        const ids: Set<string> = new Set();
        for (const value of values) {
            for (const reference of value.entityReferences) {
                ids.add(reference.id);
            }
        }
        return Array.from(ids);
    }

    #updateTextAreaHeight() {
        setTimeout(() => {
            for (let i = 0; i < this.currentEntry().componentValues.length; i++) {
                const textArea = document.getElementById(`value-input-${i}`);
                textArea.style.height = 'auto';
                textArea.style.height = `${textArea.scrollHeight}px`;
            }
        }, 10);
    }
}
