import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { IListable } from '@iqser/common-ui';
import {
    annotationDefaultColorConfig,
    annotationEntityColorConfig,
    AnnotationIconType,
    ChangeType,
    ChangeTypes,
    DefaultColors,
    Dictionary,
    Earmark,
    FalsePositiveSuperTypes,
    IComment,
    ILegalBasis,
    IManualChange,
    IPoint,
    IRectangle,
    IRedactionLogEntry,
    LogEntryEngine,
    LogEntryEngines,
    LogEntryStatuses,
    LowLevelFilterTypes,
    ManualRedactionTypes,
    SuggestionAddSuperTypes,
    SuggestionRemoveSuperTypes,
    SuggestionsSuperTypes,
    SuperType,
    SuperTypes,
} from '@red/domain';
import { annotationTypesTranslations, SuggestionAddFalsePositive } from '@translations/annotation-types-translations';
import { chronologicallyBy, timestampOf } from '../../modules/file-preview/services/file-data.service';

export class AnnotationWrapper implements IListable {
    [x: string]: unknown;

    superType: SuperType;
    typeValue: string;
    recategorizationType: string;
    color: string;
    entity: Dictionary;
    comments: IComment[] = [];
    firstTopLeftPoint: IPoint;
    id: string;
    shortContent: string;
    content: string;
    value: string;
    typeLabel: string;
    pageNumber: number;
    hint: boolean;
    redaction: boolean;
    status: string;
    dictionaryOperation: boolean;
    positions: IRectangle[];
    recommendationType: string;
    legalBasisValue: string;
    legalBasisChangeValue?: string;
    rectangle?: boolean;
    section?: string;
    reference: string[];
    imported?: boolean;
    image?: boolean;
    manual?: boolean;
    pending = false;
    hintDictionary = false;
    textAfter?: string;
    textBefore?: string;
    isChangeLogEntry = false;
    changeLogType?: 'ADDED' | 'REMOVED' | 'CHANGED';
    engines?: LogEntryEngine[];
    hasBeenResized: boolean;
    hasBeenRecategorized: boolean;
    hasLegalBasisChanged: boolean;
    hasBeenForcedHint: boolean;
    hasBeenForcedRedaction: boolean;
    hasBeenRemovedByManualOverride: boolean;
    legalBasisList: ILegalBasis[] = [];

    get searchKey(): string {
        return this.id;
    }

    get isChangeLogRemoved() {
        return this.changeLogType === 'REMOVED';
    }

    get descriptor() {
        return this.isModifyDictionary ? _('dictionary') : _('type');
    }

    get hasTextAfter() {
        return this.textAfter && this.textAfter.trim().length > 0;
    }

    get hasTextBefore() {
        return this.textBefore && this.textBefore.trim().length > 0;
    }

    get canBeMarkedAsFalsePositive() {
        return (
            (this.isRecommendation || this.superType === SuperTypes.Redaction) &&
            !this.isImage &&
            !this.imported &&
            !this.pending &&
            !this.hasBeenResized
        );
    }

    get isSuperTypeBasedColor() {
        return this.isSuggestion || this.isDeclinedSuggestion;
    }

    get isSkipped() {
        return this.superType === SuperTypes.Skipped;
    }

    get isImage() {
        return this.type?.toLowerCase() === 'image' || this.image;
    }

    get isNotSignatureImage() {
        return this.isImage && this.recategorizationType === 'signature';
    }

    get isOCR() {
        return this.type?.toLowerCase() === 'ocr';
    }

    get type() {
        return this.recategorizationType || this.typeValue;
    }

    get topLevelFilter() {
        return !LowLevelFilterTypes[this.superType];
    }

    get filterKey() {
        if (this.isEarmark) {
            return this.color;
        }

        return this.topLevelFilter ? this.superType : this.superType + this.type;
    }

    get isFalsePositive() {
        return this.type?.toLowerCase() === 'false_positive' && !!FalsePositiveSuperTypes[this.superType];
    }

    get isSuggestionAddToFalsePositive() {
        return this.typeLabel === annotationTypesTranslations[SuggestionAddFalsePositive];
    }

    get isDeclinedSuggestion() {
        return this.superType === SuperTypes.DeclinedSuggestion;
    }

    get isApproved() {
        return this.status === 'APPROVED';
    }

    get isHint() {
        return this.superType === SuperTypes.Hint;
    }

    get isEarmark() {
        return this.superType === SuperTypes.TextHighlight;
    }

    get iconShape(): AnnotationIconType {
        if (this.isRecommendation) {
            return 'hexagon';
        }

        if (this.isHint || this.isIgnoredHint) {
            return 'circle';
        }

        if (this.isSuggestion || this.isDeclinedSuggestion) {
            return 'rhombus';
        }

        return 'square';
    }

    get isDictBasedHint() {
        return this.isHint && this.engines.includes(LogEntryEngines.DICTIONARY);
    }

    get isIgnoredHint() {
        return this.superType === SuperTypes.IgnoredHint;
    }

    get isRedacted() {
        return this.superType === SuperTypes.Redaction || this.superType === SuperTypes.ManualRedaction;
    }

    get isSuggestion() {
        return !!SuggestionsSuperTypes[this.superType];
    }

    get isSuggestionResize() {
        return this.superType === SuperTypes.SuggestionResize;
    }

    get isSuggestionRecategorizeImage() {
        return this.superType === SuperTypes.SuggestionRecategorizeImage;
    }

    get isSuggestionForceHint() {
        return this.superType === SuperTypes.SuggestionForceHint;
    }

    get isSuggestionAdd() {
        return !!SuggestionAddSuperTypes[this.superType];
    }

    get isSuggestionAddDictionary() {
        return this.superType === SuperTypes.SuggestionAddDictionary;
    }

    get isSuggestionRemove() {
        return !!SuggestionRemoveSuperTypes[this.superType];
    }

    get isSuggestionRemoveDictionary() {
        return this.superType === SuperTypes.SuggestionRemoveDictionary;
    }

    get isSuggestionLegalBasisChange() {
        return this.superType === SuperTypes.SuggestionChangeLegalBasis;
    }

    get isModifyDictionary() {
        return this.dictionaryOperation;
    }

    get hasRedactionChanges(): boolean {
        return (
            this.hasBeenResized ||
            this.hasBeenRecategorized ||
            this.hasLegalBasisChanged ||
            this.hasBeenForcedHint ||
            this.hasBeenForcedRedaction ||
            this.hasBeenRemovedByManualOverride
        );
    }

    get isRecommendation() {
        return this.superType === SuperTypes.Recommendation;
    }

    get x() {
        return this.firstTopLeftPoint.x;
    }

    get y() {
        return this.firstTopLeftPoint.y;
    }

    get legalBasis() {
        return this.legalBasisChangeValue || this.legalBasisValue;
    }

    get width(): number {
        return Math.floor(this.positions[0].width);
    }

    get height(): number {
        return Math.floor(this.positions[0].height);
    }

    get previewAnnotation() {
        return (
            this.isRedacted ||
            this.isSuggestionAdd ||
            this.isSuggestionRemove ||
            this.isSuggestionResize ||
            this.isSuggestionLegalBasisChange ||
            this.isSuggestionRecategorizeImage
        );
    }

    static fromEarmark(earmark: Earmark) {
        const annotationWrapper = new AnnotationWrapper();

        annotationWrapper.id = earmark.id;
        annotationWrapper.pageNumber = earmark.positions[0].page;
        annotationWrapper.superType = SuperTypes.TextHighlight;
        annotationWrapper.typeValue = SuperTypes.TextHighlight;
        annotationWrapper.value = 'Imported';
        annotationWrapper.color = earmark.hexColor;
        annotationWrapper.positions = earmark.positions;
        annotationWrapper.firstTopLeftPoint = earmark.positions[0]?.topLeft;
        annotationWrapper.typeLabel = annotationTypesTranslations[annotationWrapper.superType];

        return annotationWrapper;
    }

    static fromData(
        redactionLogEntry: IRedactionLogEntry,
        dictionaries: Dictionary[],
        defaultColors: DefaultColors,
        changeLogType: ChangeType,
        legalBasisList: ILegalBasis[],
        hintDictionary: boolean,
        isDocumine: boolean,
    ) {
        const annotationWrapper = new AnnotationWrapper();

        annotationWrapper.id = redactionLogEntry.id;
        annotationWrapper.isChangeLogEntry = !!changeLogType;
        annotationWrapper.changeLogType = changeLogType;
        annotationWrapper.legalBasisList = legalBasisList;
        annotationWrapper.redaction = redactionLogEntry.redacted;
        annotationWrapper.hint = redactionLogEntry.hint;
        annotationWrapper.typeValue = redactionLogEntry.type;
        annotationWrapper.value = redactionLogEntry.value;
        annotationWrapper.firstTopLeftPoint = redactionLogEntry.positions[0]?.topLeft;
        annotationWrapper.pageNumber = redactionLogEntry.positions[0]?.page;
        annotationWrapper.positions = redactionLogEntry.positions;
        annotationWrapper.textBefore = redactionLogEntry.textBefore;
        annotationWrapper.textAfter = redactionLogEntry.textAfter;
        annotationWrapper.dictionaryOperation = redactionLogEntry.dictionaryEntry;
        annotationWrapper.image = redactionLogEntry.image;
        annotationWrapper.imported = redactionLogEntry.imported;
        annotationWrapper.legalBasisValue = redactionLogEntry.legalBasis;
        annotationWrapper.comments = redactionLogEntry.comments || [];
        annotationWrapper.manual = redactionLogEntry.manualChanges?.length > 0;
        annotationWrapper.engines = redactionLogEntry.engines;
        annotationWrapper.section = redactionLogEntry.section;
        annotationWrapper.reference = redactionLogEntry.reference || [];
        annotationWrapper.rectangle = redactionLogEntry.rectangle;
        annotationWrapper.hintDictionary = hintDictionary;
        annotationWrapper.hasBeenResized = !!redactionLogEntry.manualChanges?.find(
            c => c.manualRedactionType === ManualRedactionTypes.RESIZE && c.annotationStatus === LogEntryStatuses.APPROVED,
        );
        annotationWrapper.hasBeenRecategorized = !!redactionLogEntry.manualChanges?.find(
            c => c.manualRedactionType === ManualRedactionTypes.RECATEGORIZE && c.annotationStatus === LogEntryStatuses.APPROVED,
        );
        annotationWrapper.hasLegalBasisChanged = !!redactionLogEntry.manualChanges?.find(
            c => c.manualRedactionType === ManualRedactionTypes.LEGAL_BASIS_CHANGE && c.annotationStatus === LogEntryStatuses.APPROVED,
        );
        annotationWrapper.hasBeenForcedHint = !!redactionLogEntry.manualChanges?.find(
            c => c.manualRedactionType === ManualRedactionTypes.FORCE_HINT && c.annotationStatus === LogEntryStatuses.APPROVED,
        );
        annotationWrapper.hasBeenForcedRedaction = !!redactionLogEntry.manualChanges?.find(
            c => c.manualRedactionType === ManualRedactionTypes.FORCE_REDACT && c.annotationStatus === LogEntryStatuses.APPROVED,
        );
        annotationWrapper.hasBeenRemovedByManualOverride = !!redactionLogEntry.manualChanges?.find(
            c => c.manualRedactionType === ManualRedactionTypes.REMOVE_LOCALLY && c.annotationStatus === LogEntryStatuses.APPROVED,
        );
        annotationWrapper.legalBasisChangeValue = redactionLogEntry.manualChanges?.find(
            c => c.manualRedactionType === ManualRedactionTypes.LEGAL_BASIS_CHANGE && c.annotationStatus === LogEntryStatuses.REQUESTED,
        )?.propertyChanges.legalBasis;

        this.#createContent(annotationWrapper, redactionLogEntry, isDocumine);
        this.#setSuperType(annotationWrapper, redactionLogEntry);
        this.#handleRecommendations(annotationWrapper, redactionLogEntry);
        annotationWrapper.typeLabel = this.#getTypeLabel(redactionLogEntry, annotationWrapper);

        const entity = dictionaries.find(d => d.type === annotationWrapper.typeValue);
        annotationWrapper.entity = entity?.virtual ? null : entity;

        let colorKey = annotationWrapper.isSuperTypeBasedColor
            ? annotationDefaultColorConfig[annotationWrapper.superType]
            : annotationEntityColorConfig[annotationWrapper.superType];

        if (annotationWrapper.isSuperTypeBasedColor && annotationWrapper.isSuggestionResize && !annotationWrapper.isModifyDictionary) {
            colorKey = annotationDefaultColorConfig[SuperTypes.SuggestionAdd];
        }

        annotationWrapper.color = annotationWrapper.isSuperTypeBasedColor ? defaultColors[colorKey] : (entity[colorKey] as string);

        return annotationWrapper;
    }

    static #getTypeLabel(redactionLogEntry: IRedactionLogEntry, annotation: AnnotationWrapper): string {
        if (redactionLogEntry.reason?.toLowerCase() === 'false positive') {
            return annotationTypesTranslations[SuggestionAddFalsePositive];
        }
        if (annotation.superType === SuperTypes.ManualRedaction && annotation.hintDictionary) {
            return _('annotation-type.manual-hint');
        }
        return annotationTypesTranslations[annotation.superType];
    }

    static #handleRecommendations(annotationWrapper: AnnotationWrapper, redactionLogEntry: IRedactionLogEntry) {
        if (annotationWrapper.superType === SuperTypes.Recommendation) {
            annotationWrapper.recommendationType = redactionLogEntry.type;
        }
    }

    static #setSuperType(annotationWrapper: AnnotationWrapper, entry: IRedactionLogEntry) {
        if (entry.manualChanges?.length) {
            const lastRelevantManualChange = entry.manualChanges?.at(-1);
            const viableChanges = entry.changes.filter(c => c.analysisNumber > 1);
            const lastChange = viableChanges.sort(chronologicallyBy(x => x.dateTime)).at(-1);
            const lastChangeOccurredAfterLastManualChange =
                lastChange && timestampOf(lastChange.dateTime) > timestampOf(lastRelevantManualChange.processedDate);

            if (lastChangeOccurredAfterLastManualChange && lastChange.type === ChangeTypes.ADDED && entry.redacted) {
                annotationWrapper.superType = SuperTypes.Redaction;
                return;
            }

            annotationWrapper.pending = !lastRelevantManualChange.processed;

            annotationWrapper.superType = this.#selectSuperType(entry, lastRelevantManualChange, annotationWrapper.hintDictionary);

            if (lastRelevantManualChange.annotationStatus === LogEntryStatuses.REQUESTED) {
                annotationWrapper.recategorizationType = lastRelevantManualChange.propertyChanges.type;
            }
        } else {
            if (entry.recommendation) {
                annotationWrapper.superType = SuperTypes.Recommendation;
            } else if (entry.redacted) {
                annotationWrapper.superType = SuperTypes.Redaction;
            } else if (entry.hint) {
                annotationWrapper.superType = SuperTypes.Hint;
            } else {
                annotationWrapper.superType = SuperTypes.Skipped;
            }
        }
    }

    static #createContent(annotationWrapper: AnnotationWrapper, entry: IRedactionLogEntry, isDocumine: boolean) {
        let content = '';
        if (entry.matchedRule) {
            content += `Rule ${entry.matchedRule} matched${isDocumine ? ':' : ''} \n\n`;
        }

        if (entry.reason) {
            if (isDocumine && entry.reason.slice(-1) === '.') {
                entry.reason = entry.reason.slice(0, -1);
            }

            content += entry.reason + '\n\n';
            //remove leading and trailing commas and whitespaces
            content = content.replace(/(^[, ]*)|([, ]*$)/g, '');
            content = content.substring(0, 1).toUpperCase() + content.substring(1);
        }

        if (annotationWrapper.legalBasis && !isDocumine) {
            content += 'Legal basis: ' + annotationWrapper.legalBasis + '\n\n';
        }

        if (annotationWrapper.hasBeenRemovedByManualOverride) {
            content += 'Removed by manual override';
        }

        if (entry.section) {
            let prefix = `In section${isDocumine ? '' : ':'} `;
            if (content.length) {
                prefix = ` ${prefix.toLowerCase()}`;
            }
            content += `${prefix} "${entry.section}"`;
        }

        annotationWrapper.shortContent = this.#getShortContent(annotationWrapper) || content;
        annotationWrapper.content = content;
    }

    static #getShortContent(annotationWrapper: AnnotationWrapper) {
        if (annotationWrapper.legalBasis) {
            const lb = annotationWrapper.legalBasisList.find(
                lbm => lbm.reason?.toLowerCase().includes(annotationWrapper.legalBasis.toLowerCase()),
            );
            if (lb) {
                return lb.name;
            }
        }

        return annotationWrapper.legalBasis;
    }

    static #selectSuperType(redactionLogEntry: IRedactionLogEntry, lastManualChange: IManualChange, isHintDictionary: boolean): SuperType {
        switch (lastManualChange.manualRedactionType) {
            case ManualRedactionTypes.ADD_LOCALLY:
                switch (lastManualChange.annotationStatus) {
                    case LogEntryStatuses.APPROVED:
                        return SuperTypes.ManualRedaction;
                    case LogEntryStatuses.DECLINED:
                        return SuperTypes.DeclinedSuggestion;
                    case LogEntryStatuses.REQUESTED:
                        return SuperTypes.SuggestionAdd;
                }
                break;
            case ManualRedactionTypes.ADD_TO_DICTIONARY:
                switch (lastManualChange.annotationStatus) {
                    case LogEntryStatuses.APPROVED:
                        return isHintDictionary ? SuperTypes.Hint : SuperTypes.Redaction;
                    case LogEntryStatuses.DECLINED:
                        return SuperTypes.DeclinedSuggestion;
                    case LogEntryStatuses.REQUESTED:
                        return SuperTypes.SuggestionAddDictionary;
                }
                break;
            case ManualRedactionTypes.REMOVE_LOCALLY:
                switch (lastManualChange.annotationStatus) {
                    case LogEntryStatuses.APPROVED:
                        return isHintDictionary ? SuperTypes.IgnoredHint : SuperTypes.Skipped;
                    case LogEntryStatuses.DECLINED: {
                        if (isHintDictionary) {
                            return SuperTypes.Hint;
                        }

                        if (redactionLogEntry.redacted) {
                            return SuperTypes.Redaction;
                        }

                        return SuperTypes.Skipped;
                    }
                    case LogEntryStatuses.REQUESTED:
                        return SuperTypes.SuggestionRemove;
                }
                break;
            case ManualRedactionTypes.REMOVE_FROM_DICTIONARY:
                if (redactionLogEntry.redacted) {
                    if (lastManualChange.processed) {
                        switch (lastManualChange.annotationStatus) {
                            case LogEntryStatuses.APPROVED:
                                return SuperTypes.Skipped;
                            case LogEntryStatuses.DECLINED:
                                return SuperTypes.Redaction;
                            case LogEntryStatuses.REQUESTED:
                                return SuperTypes.SuggestionRemoveDictionary;
                        }
                    } else {
                        switch (lastManualChange.annotationStatus) {
                            case LogEntryStatuses.APPROVED:
                            case LogEntryStatuses.DECLINED:
                                return SuperTypes.Redaction;
                            case LogEntryStatuses.REQUESTED:
                                return SuperTypes.SuggestionRemoveDictionary;
                        }
                    }
                } else {
                    if (lastManualChange.processed) {
                        switch (lastManualChange.annotationStatus) {
                            case LogEntryStatuses.APPROVED:
                                return redactionLogEntry.recommendation ? SuperTypes.Recommendation : SuperTypes.Skipped;
                            case LogEntryStatuses.DECLINED:
                                return isHintDictionary ? SuperTypes.Hint : SuperTypes.Skipped;
                            case LogEntryStatuses.REQUESTED:
                                return SuperTypes.SuggestionRemoveDictionary;
                        }
                    } else {
                        switch (lastManualChange.annotationStatus) {
                            case LogEntryStatuses.APPROVED:
                            case LogEntryStatuses.DECLINED:
                                return isHintDictionary ? SuperTypes.Hint : SuperTypes.Skipped;
                            case LogEntryStatuses.REQUESTED:
                                return SuperTypes.SuggestionRemoveDictionary;
                        }
                    }
                }
                break;
            case ManualRedactionTypes.FORCE_REDACT:
                switch (lastManualChange.annotationStatus) {
                    case LogEntryStatuses.APPROVED:
                        return SuperTypes.Redaction;
                    case LogEntryStatuses.DECLINED:
                        return isHintDictionary ? SuperTypes.IgnoredHint : SuperTypes.Skipped;
                    case LogEntryStatuses.REQUESTED:
                        return SuperTypes.SuggestionForceRedaction;
                }
                break;
            case ManualRedactionTypes.FORCE_HINT:
                switch (lastManualChange.annotationStatus) {
                    case LogEntryStatuses.APPROVED:
                        return SuperTypes.Hint;
                    case LogEntryStatuses.DECLINED:
                        return SuperTypes.IgnoredHint;
                    case LogEntryStatuses.REQUESTED:
                        return SuperTypes.SuggestionForceHint;
                }
                break;
            case ManualRedactionTypes.RECATEGORIZE:
                switch (lastManualChange.annotationStatus) {
                    case LogEntryStatuses.APPROVED:
                    case LogEntryStatuses.DECLINED: {
                        if (redactionLogEntry.recommendation) {
                            return SuperTypes.Recommendation;
                        } else if (redactionLogEntry.redacted) {
                            return SuperTypes.Redaction;
                        } else if (redactionLogEntry.hint) {
                            return SuperTypes.Hint;
                        }
                        return isHintDictionary ? SuperTypes.IgnoredHint : SuperTypes.Skipped;
                    }
                    case LogEntryStatuses.REQUESTED:
                        return SuperTypes.SuggestionRecategorizeImage;
                }
                break;
            case ManualRedactionTypes.LEGAL_BASIS_CHANGE:
                switch (lastManualChange.annotationStatus) {
                    case LogEntryStatuses.APPROVED:
                    case LogEntryStatuses.DECLINED:
                        return redactionLogEntry.type === SuperTypes.ManualRedaction ? SuperTypes.ManualRedaction : SuperTypes.Redaction;
                    case LogEntryStatuses.REQUESTED:
                        return SuperTypes.SuggestionChangeLegalBasis;
                }
                break;
            case ManualRedactionTypes.RESIZE:
                switch (lastManualChange.annotationStatus) {
                    case LogEntryStatuses.APPROVED:
                    case LogEntryStatuses.DECLINED:
                        if (redactionLogEntry.recommendation) {
                            return SuperTypes.Recommendation;
                        } else if (redactionLogEntry.redacted) {
                            return redactionLogEntry.type === SuperTypes.ManualRedaction
                                ? SuperTypes.ManualRedaction
                                : SuperTypes.Redaction;
                        } else if (redactionLogEntry.hint) {
                            return SuperTypes.Hint;
                        }
                        return isHintDictionary ? SuperTypes.IgnoredHint : SuperTypes.Skipped;

                    case LogEntryStatuses.REQUESTED:
                        return SuperTypes.SuggestionResize;
                }
                break;
        }
    }
}
