import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { IListable } from '@iqser/common-ui';
import {
    annotationDefaultColorConfig,
    annotationEntityColorConfig,
    AnnotationIconType,
    ChangeType,
    DefaultColors,
    Dictionary,
    Earmark,
    EntityTypes,
    EntryStates,
    FalsePositiveSuperTypes,
    IEntityLogEntry,
    ILegalBasis,
    IPoint,
    IRectangle,
    LogEntryEngine,
    LogEntryEngines,
    LowLevelFilterTypes,
    ManualRedactionType,
    ManualRedactionTypes,
    SuperType,
    SuperTypeMapper,
    SuperTypes,
} from '@red/domain';
import { annotationTypesTranslations } from '@translations/annotation-types-translations';

interface AnnotationContent {
    translation: string;
    params: { [key: string]: string };
    untranslatedContent: string;
}

export class AnnotationWrapper implements IListable {
    id: string;
    superType: SuperType;
    superTypeLabel: string;
    type: string;
    typeLabel?: string;
    color: string;
    numberOfComments = 0;
    firstTopLeftPoint: IPoint;
    shortContent: string;
    content: AnnotationContent;
    value: string;
    pageNumber: number;
    dictionaryOperation = false;
    positions: IRectangle[] = [];
    legalBasisValue: string;
    // AREA === rectangle
    AREA = false;
    HINT = false;
    IMAGE = false;
    IMAGE_HINT = false;
    section?: string;
    reference: string[] = [];
    imported = false;
    manual = false;
    pending = false;
    textAfter?: string;
    textBefore?: string;
    isChangeLogEntry = false;
    engines: LogEntryEngine[] = [];
    hasBeenResized: boolean;
    hasBeenResizedLocally: boolean;
    hasBeenRecategorized: boolean;
    hasLegalBasisChanged: boolean;
    hasBeenForcedHint: boolean;
    hasBeenForcedRedaction: boolean;
    hasBeenRemovedByManualOverride: boolean;
    isRemoved = false;
    isRemovedLocally = false;
    hiddenInWorkload = false;
    lastManualChange: ManualRedactionType;
    entry: IEntityLogEntry;

    get isRuleBased() {
        return this.engines.includes(LogEntryEngines.RULE);
    }

    get isDictBased() {
        return [LogEntryEngines.DICTIONARY, LogEntryEngines.DOSSIER_DICTIONARY].some(engine => this.engines.includes(engine));
    }

    get isRedactedImageHint() {
        return this.IMAGE_HINT && this.superType === SuperTypes.Redaction;
    }

    get isSkippedImageHint() {
        return this.IMAGE_HINT && this.superType === SuperTypes.Hint;
    }

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

    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.isSkipped && this.isDictBased)) &&
            !this.isImage &&
            !this.imported &&
            !this.pending &&
            !this.hasBeenResized
        );
    }

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

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

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

    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 isHint() {
        return this.superType === SuperTypes.Hint || this.superType === SuperTypes.ManualHint;
    }

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

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

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

        if (this.HINT) {
            return 'circle';
        }

        return 'square';
    }

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

    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.legalBasisValue;
    }

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

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

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

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

        return annotationWrapper;
    }

    static fromData(
        logEntry: IEntityLogEntry,
        dictionary: Dictionary,
        changeLogType: ChangeType,
        legalBasisList: ILegalBasis[],
        isDocumine: boolean,
        defaultColors: DefaultColors,
    ) {
        const annotationWrapper = new AnnotationWrapper();

        annotationWrapper.pending = logEntry.state === EntryStates.PENDING;
        annotationWrapper.id = logEntry.id + (annotationWrapper.pending ? '-pending' : '');
        annotationWrapper.isChangeLogEntry = logEntry.state === EntryStates.REMOVED || !!changeLogType;
        annotationWrapper.type = logEntry.type;
        annotationWrapper.value = logEntry.value;
        annotationWrapper.firstTopLeftPoint = { x: logEntry.positions[0].rectangle[0], y: logEntry.positions[0].rectangle[1] };
        annotationWrapper.pageNumber = logEntry.positions[0].pageNumber;
        annotationWrapper.positions = logEntry.positions.map(p => ({
            page: p.pageNumber,
            height: p.rectangle[3],
            width: p.rectangle[2],
            topLeft: { x: p.rectangle[0], y: p.rectangle[1] },
        }));
        annotationWrapper.textBefore = logEntry.textBefore;
        annotationWrapper.textAfter = logEntry.textAfter;
        annotationWrapper.dictionaryOperation = logEntry.dictionaryEntry;

        annotationWrapper.HINT = logEntry.entryType === EntityTypes.HINT;
        annotationWrapper.IMAGE = logEntry.entryType === EntityTypes.IMAGE;
        annotationWrapper.AREA = logEntry.entryType === EntityTypes.AREA;
        annotationWrapper.IMAGE_HINT = logEntry.entryType === EntityTypes.IMAGE_HINT;

        annotationWrapper.numberOfComments = logEntry.numberOfComments;
        annotationWrapper.imported = logEntry.imported;
        annotationWrapper.legalBasisValue = logEntry.legalBasis;
        annotationWrapper.manual = logEntry.manualChanges?.length > 0;
        annotationWrapper.engines = logEntry.engines ?? [];
        annotationWrapper.section = logEntry.section;
        annotationWrapper.reference = logEntry.reference || [];
        annotationWrapper.hasBeenResized = !!logEntry.manualChanges?.find(c => c.manualRedactionType === ManualRedactionTypes.RESIZE);
        annotationWrapper.hasBeenResizedLocally =
            annotationWrapper.hasBeenResized && annotationWrapper.engines.includes(LogEntryEngines.MANUAL);
        annotationWrapper.hasBeenRecategorized = !!logEntry.manualChanges?.find(
            c => c.manualRedactionType === ManualRedactionTypes.RECATEGORIZE,
        );
        annotationWrapper.hasLegalBasisChanged = !!logEntry.manualChanges?.find(
            c => c.manualRedactionType === ManualRedactionTypes.LEGAL_BASIS_CHANGE,
        );
        annotationWrapper.hasBeenForcedHint =
            !!logEntry.manualChanges?.find(c => c.manualRedactionType === ManualRedactionTypes.FORCE) && annotationWrapper.HINT;
        annotationWrapper.hasBeenForcedRedaction =
            !!logEntry.manualChanges?.find(c => c.manualRedactionType === ManualRedactionTypes.FORCE) && !annotationWrapper.HINT;
        annotationWrapper.hasBeenRemovedByManualOverride = !!logEntry.manualChanges?.find(
            c => c.manualRedactionType === ManualRedactionTypes.REMOVE,
        );

        const content = this.#createContent(annotationWrapper, logEntry, isDocumine);
        annotationWrapper.shortContent = this.#getShortContent(annotationWrapper, legalBasisList) || content.untranslatedContent;
        annotationWrapper.content = content;

        const lastRelevantManualChange = logEntry.manualChanges?.at(-1);
        annotationWrapper.lastManualChange = lastRelevantManualChange?.manualRedactionType;
        annotationWrapper.superType = SuperTypeMapper[logEntry.entryType][logEntry.state](logEntry);
        annotationWrapper.superTypeLabel = annotationTypesTranslations[annotationWrapper.superType];

        annotationWrapper.isRemoved = logEntry.state === EntryStates.REMOVED;
        annotationWrapper.isRemovedLocally =
            lastRelevantManualChange?.manualRedactionType === ManualRedactionTypes.REMOVE &&
            logEntry.engines.includes(LogEntryEngines.MANUAL);

        annotationWrapper.typeLabel = dictionary?.virtual ? undefined : dictionary?.label;

        if (annotationWrapper.pending) {
            annotationWrapper.color = defaultColors[annotationDefaultColorConfig.analysis] as string;
        } else {
            const colorKey = annotationEntityColorConfig[annotationWrapper.superType];
            const defaultColor = annotationDefaultColorConfig[annotationWrapper.superType];
            annotationWrapper.color =
                dictionary && !annotationWrapper.isRedactedImageHint
                    ? (dictionary[colorKey] as string)
                    : (defaultColors[defaultColor] as string);
        }

        annotationWrapper.entry = logEntry;

        return annotationWrapper;
    }

    static #createContent(annotationWrapper: AnnotationWrapper, logEntry: IEntityLogEntry, isDocumine: boolean) {
        let untranslatedContent = '';
        const params: { [key: string]: string } = {};
        if (logEntry.matchedRule) {
            params['hasRule'] = 'true';
            params['matchedRule'] = logEntry.matchedRule.replace(/(^[, ]*)|([, ]*$)/g, '');
            params['ruleSymbol'] = isDocumine ? ':' : '';

            untranslatedContent += `Rule ${logEntry.matchedRule} matched${isDocumine ? ':' : ''} \n\n`;
        }

        if (logEntry.reason) {
            params['hasReason'] = 'true';
            if (isDocumine && logEntry.reason.slice(-1) === '.') {
                logEntry.reason = logEntry.reason.slice(0, -1);
            }
            if (!params['hasRule']) {
                params['reason'] = logEntry.reason.substring(0, 1).toUpperCase() + logEntry.reason.substring(1);
            } else {
                params['reason'] = logEntry.reason;
            }
            params['reason'] = params['reason'].replace(/(^[, ]*)|([, ]*$)/g, '');
            untranslatedContent += logEntry.reason + '\n\n';
            //remove leading and trailing commas and whitespaces
            untranslatedContent = untranslatedContent.replace(/(^[, ]*)|([, ]*$)/g, '');
            untranslatedContent = untranslatedContent.substring(0, 1).toUpperCase() + untranslatedContent.substring(1);
        }

        if (annotationWrapper.legalBasis && !isDocumine) {
            params['hasLb'] = 'true';
            params['legalBasis'] = annotationWrapper.legalBasis;
            untranslatedContent += 'Legal basis: ' + annotationWrapper.legalBasis + '\n\n';
        }

        if (annotationWrapper.hasBeenRemovedByManualOverride) {
            params['hasOverride'] = 'true';
            untranslatedContent += 'Removed by manual override';
        }

        if (logEntry.section) {
            params['hasSection'] = 'true';
            params['sectionSymbol'] = isDocumine ? '' : ':';
            params['shouldLower'] = untranslatedContent.length.toString();
            params['section'] = logEntry.section;
            let prefix = `In section${isDocumine ? '' : ':'} `;
            if (untranslatedContent.length) {
                prefix = ` ${prefix.toLowerCase()}`;
            }
            untranslatedContent += `${prefix} "${logEntry.section}"`;
        }

        return { translation: _('annotation-content'), params: params, untranslatedContent: untranslatedContent };
    }

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

        return annotationWrapper.legalBasis;
    }
}
