import { Injectable, signal } from '@angular/core';
import { Core } from '@pdftron/webviewer';
import type { List } from '@iqser/common-ui/lib/utils';
import { AnnotationPredicate, DeleteAnnotationsOptions } from '../utils/types';
import { AnnotationWrapper } from '@models/file/annotation.wrapper';
import { fromEvent, Observable } from 'rxjs';
import { tap } from 'rxjs/operators';
import { asList, getId, isStringOrWrapper } from '../utils/functions';
import { getLast } from '@utils/functions';
import { AnnotationToolNames } from '../utils/constants';
import AnnotationManager = Core.AnnotationManager;
import Annotation = Core.Annotations.Annotation;

@Injectable()
export class REDAnnotationManager {
    readonly #hidden = signal(new Set<string>());
    #manager: AnnotationManager;
    annotationSelected$: Observable<[Annotation[], string]>;
    resizingAnnotationId?: string = undefined;
    readonly hidden = this.#hidden.asReadonly();

    get selected() {
        return this.#manager.getSelectedAnnotations();
    }

    get annotations() {
        return this.#get();
    }

    get #annotationSelected$() {
        const onSelect$ = fromEvent<[Annotation[], string]>(this.#manager, 'annotationSelected');
        return onSelect$.pipe(tap(value => console.log('Annotation selected: ', value)));
    }

    addToHidden(value: string) {
        this.#hidden.mutate(set => set.add(value));
    }

    removeFromHidden(value: string) {
        this.#hidden.mutate(set => set.delete(value));
    }

    init(annotationManager: AnnotationManager) {
        this.#manager = annotationManager;
        this.annotationSelected$ = this.#annotationSelected$;
        this.#autoSelectRectangleAfterCreation();
    }

    isHidden(annotationId: string) {
        return this.hidden().has(annotationId);
    }

    delete(annotations?: List | List<AnnotationWrapper> | string | AnnotationWrapper) {
        const items = isStringOrWrapper(annotations) ? [this.get(annotations)] : this.get(annotations);
        if (!items.length) {
            return;
        }
        const options: DeleteAnnotationsOptions = { force: true };
        this.#manager.deleteAnnotations(items, options);
    }

    get(annotation: AnnotationWrapper | string): Annotation;
    get(annotations: List | List<AnnotationWrapper>): Annotation[];
    get(predicate?: (value: Annotation) => boolean): Annotation[];
    get(argument?: AnnotationPredicate | List<AnnotationWrapper> | List | AnnotationWrapper | string): Annotation | Annotation[] {
        if (isStringOrWrapper(argument)) {
            return this.#getById(argument);
        }

        const isList = argument instanceof Array;
        return isList ? this.#getByIds(argument) : this.#get(argument);
    }

    deselect(annotation: string | AnnotationWrapper);
    deselect(annotations?: List | List<AnnotationWrapper>);
    deselect(argument?: string | AnnotationWrapper | List | List<AnnotationWrapper>) {
        if (!argument) {
            return this.#manager.deselectAllAnnotations();
        }

        const ann = isStringOrWrapper(argument) ? [this.#getById(argument)] : this.#getByIds(argument);
        this.#manager.deselectAnnotations(ann);
    }

    hide(annotations: Annotation[]): void {
        this.#manager.hideAnnotations(annotations);
    }

    show(annotations: Annotation[]): void {
        this.#manager.showAnnotations(annotations);
    }

    jumpAndSelect(annotations: List | List<AnnotationWrapper>) {
        const annotationsFromViewer = this.get(annotations);

        this.#manager.jumpToAnnotation(getLast(annotationsFromViewer));
        this.#manager.selectAnnotations(annotationsFromViewer);
    }

    select(annotations: Annotation | Annotation[]) {
        this.#manager.selectAnnotations(asList(annotations));
    }

    redraw(annotations: Annotation | Annotation[]) {
        annotations = asList(annotations);
        annotations.forEach(annotation => this.#manager.redrawAnnotation(annotation));
    }

    add(annotations: Annotation | Annotation[]) {
        try {
            annotations = asList(annotations);
            this.#manager.addAnnotations(annotations, { imported: true });
            return this.#manager.drawAnnotationsFromList(annotations);
        } catch (e) {
            console.log(e);
        }
    }

    showHidden() {
        const hidden = this.#getByIds([...this.hidden().values()]);
        this.show(hidden);
    }

    #getById(annotation: AnnotationWrapper | string) {
        return this.#manager.getAnnotationById(getId(annotation));
    }

    #autoSelectRectangleAfterCreation() {
        this.#manager.addEventListener('annotationChanged', (annotations: Annotation[]) => {
            // when a rectangle is drawn,
            // it returns one annotation with tool name 'AnnotationCreateRectangle;
            // this will auto select rectangle after drawing
            if (annotations.length === 1 && annotations[0].ToolName === AnnotationToolNames.AnnotationCreateRectangle) {
                this.#manager.selectAnnotations(annotations);
                annotations[0].disableRotationControl();
            }
        });
    }

    #getByIds(annotations: List | List<AnnotationWrapper>) {
        return annotations.map((item: string | AnnotationWrapper) => this.#getById(item)).filter(a => !!a);
    }

    #get(predicate?: AnnotationPredicate) {
        const annotations = this.#manager.getAnnotationsList();
        return predicate ? annotations.filter(predicate) : annotations;
    }
}
