import { Injectable } from '@angular/core';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { getConfig } from '@iqser/common-ui';
import { Filter, handleCheckedValue, IFilter, INestedFilter, NestedFilter } from '@iqser/common-ui/lib/filtering';
import { AnnotationWrapper } from '@models/file/annotation.wrapper';
import { annotationDefaultColorConfig } from '@red/domain';
import { DefaultColorsService } from '@services/entity-services/default-colors.service';
import { ViewedPagesMapService } from '@services/files/viewed-pages-map.service';
import { annotationTypesTranslations } from '@translations/annotation-types-translations';
import { UserPreferenceService } from '@users/user-preference.service';
import { of } from 'rxjs';
import { SuperTypeSorter } from '../../../utils';
import { PageRotationService } from '../../pdf-viewer/services/page-rotation.service';
import {
    sortBottomLeftToTopRight,
    sortBottomRightToTopLeft,
    sortTopLeftToBottomRight,
    sortTopRightToBottomLeft,
} from '../utils/sort-by-page-rotation.utils';
import { FileDataService } from './file-data.service';
import { FilePreviewStateService } from './file-preview-state.service';

@Injectable()
export class AnnotationProcessingService {
    readonly #isDocumine = getConfig().IS_DOCUMINE;
    readonly #devMode = this._userPreferenceService.isIqserDevMode;

    constructor(
        private readonly _viewedPagesMapService: ViewedPagesMapService,
        private readonly _fileDataService: FileDataService,
        private readonly _state: FilePreviewStateService,
        private readonly _defaultColorsService: DefaultColorsService,
        private readonly _pageRotationService: PageRotationService,
        private readonly _userPreferenceService: UserPreferenceService,
    ) {}

    get secondaryAnnotationFilters(): INestedFilter[] {
        return [
            {
                id: 'with-comments',
                icon: 'red:comment',
                label: _('filter-menu.with-comments'),
                checked: false,
                topLevelFilter: true,
                checker: (annotation: AnnotationWrapper) => annotation?.comments?.length > 0,
            },
            {
                id: 'redaction-changes',
                icon: 'red:redaction-changes',
                label: _('filter-menu.redaction-changes'),
                checked: false,
                topLevelFilter: true,
                checker: (annotation: AnnotationWrapper) => annotation?.hasRedactionChanges,
            },
            {
                id: 'unseen-pages',
                icon: 'iqser:pages',
                label: _('filter-menu.unseen-pages'),
                checked: false,
                topLevelFilter: true,
                checker: (annotation: AnnotationWrapper) =>
                    !this._viewedPagesMapService.get(this._state.fileId).some(page => page.page === annotation.pageNumber),
            },
            {
                id: 'pages-without-annotations',
                icon: 'iqser:pages',
                label: _('filter-menu.pages-without-annotations'),
                checked: false,
                topLevelFilter: true,
                checker: () => true,
            },
        ].map(item => new NestedFilter(item));
    }

    getAnnotationFilter(): INestedFilter[] {
        const filterMap = new Map<string, INestedFilter>();
        const filters: INestedFilter[] = [];

        this._fileDataService.all?.forEach(a => {
            if (this.#isDocumine && !this.#devMode && a.isOCR) {
                return;
            }
            const topLevelFilter = a.topLevelFilter;
            const filter = filterMap.get(a.filterKey);
            if (filter) {
                filter.matches += 1;
            } else {
                // top level filter
                if (topLevelFilter) {
                    this._createParentFilter(a.isEarmark ? a.filterKey : a.superType, filterMap, filters, a.isEarmark, {
                        color$: of(a.color),
                        shortLabel: a.isEarmark ? '' : null,
                        shape: a.iconShape,
                    });
                } else {
                    let parentFilter = filterMap.get(a.superType);

                    if (!parentFilter) {
                        parentFilter = this._createParentFilter(a.superType, filterMap, filters, false, {
                            shape: a.iconShape,
                            color$: this._defaultColorsService.getColor$(
                                this._state.dossierTemplateId,
                                annotationDefaultColorConfig[a.superType],
                            ),
                        });
                    }
                    const childFilter: IFilter = {
                        id: a.filterKey,
                        label: a.entity.label,
                        checked: false,
                        matches: 1,
                        metadata: {
                            color$: of(a.color),
                            shape: a.iconShape,
                        },
                        skipTranslation: true,
                    };

                    const newChildFilter = new Filter(childFilter);
                    filterMap.set(a.filterKey, newChildFilter);
                    parentFilter.children.push(newChildFilter);
                }
            }
        });

        for (const filter of filters) {
            filter.children.sort((a, b) => a.id.localeCompare(b.id));
            handleCheckedValue(filter);
            if (filter.checked || filter.indeterminate) {
                filter.expanded = true;
            }
            if (filter.children.length > 0) {
                filter.matches = filter.children.reduce((a, b) => a + b.matches, 0);
            }
        }

        return filters.sort((a, b) => SuperTypeSorter[a.id] - SuperTypeSorter[b.id]);
    }

    filterAndGroupAnnotations(
        annotations: AnnotationWrapper[],
        primaryFilters: INestedFilter[],
        secondaryFilters?: INestedFilter[],
    ): Map<number, AnnotationWrapper[]> {
        const obj = new Map<number, AnnotationWrapper[]>();

        const primaryFlatFilters = this._getFlatFilters(primaryFilters, f => f.checked);
        const secondaryFlatFilters = this._getFlatFilters(secondaryFilters, f => f.checked);

        for (const annotation of annotations) {
            if (!this._matchesOne(primaryFlatFilters, f => this._checkByFilterKey(f, annotation))) {
                continue;
            }

            if (
                !this._matchesAll(
                    secondaryFlatFilters,
                    f => (!!f.checker && f.checker(annotation)) || this._checkByFilterKey(f, annotation),
                )
            ) {
                continue;
            }

            const pageNumber = annotation.pageNumber;

            if (!obj.has(pageNumber)) {
                obj.set(pageNumber, []);
            }

            obj.get(pageNumber).push(annotation);
        }

        obj.forEach((values, page) => {
            if (!values[0].isEarmark) {
                obj.set(page, this.#sortAnnotations(values));
            }
        });

        return obj;
    }

    private _createParentFilter(
        key: string,
        filterMap: Map<string, INestedFilter>,
        filters: INestedFilter[],
        skipTranslation = false,
        metadata?: Record<string, any>,
    ) {
        const filter: INestedFilter = new NestedFilter({
            id: key,
            topLevelFilter: true,
            matches: 1,
            label: skipTranslation ? key : annotationTypesTranslations[key],
            skipTranslation,
            metadata,
        });
        filterMap.set(key, filter);
        filters.push(filter);
        return filter;
    }

    private _getFlatFilters(filters: INestedFilter[], filterBy?: (f: INestedFilter) => boolean) {
        const flatFilters: INestedFilter[] = [];

        filters.forEach(filter => {
            flatFilters.push(filter);
            flatFilters.push(...filter.children);
        });

        return filterBy ? flatFilters.filter(f => filterBy(f)) : flatFilters;
    }

    private _matchesOne = (filters: INestedFilter[], condition: (filter: INestedFilter) => boolean): boolean => {
        if (filters.length === 0) {
            return true;
        }

        for (const filter of filters) {
            if (condition(filter)) {
                return true;
            }
        }

        return false;
    };

    private _matchesAll = (filters: INestedFilter[], condition: (filter: INestedFilter) => boolean): boolean => {
        if (filters.length === 0) {
            return true;
        }

        for (const filter of filters) {
            if (!condition(filter)) {
                return false;
            }
        }

        return true;
    };

    private _checkByFilterKey = (filter: NestedFilter | IFilter, annotation: AnnotationWrapper) => filter.id === annotation.filterKey;

    #sortAnnotations(annotations: AnnotationWrapper[]): AnnotationWrapper[] {
        const pageRotation = this._pageRotationService.currentPageRotation;
        return annotations.sort((first, second) => {
            if (first.pageNumber === second.pageNumber) {
                switch (pageRotation) {
                    case 0: {
                        return sortTopLeftToBottomRight(first, second);
                    }
                    case 90: {
                        return sortBottomLeftToTopRight(first, second);
                    }
                    case 180: {
                        return sortBottomRightToTopLeft(first, second);
                    }
                    case 270: {
                        return sortTopRightToBottomLeft(first, second);
                    }
                }
            }
            return first.pageNumber < second.pageNumber ? -1 : 1;
        });
    }
}
