import { HttpErrorResponse, HttpStatusCode } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { GenericService, IqserPermissionsService, Toaster } from '@iqser/common-ui';
import { List } from '@iqser/common-ui/lib/utils';
import { type AnnotationWrapper } from '@models/file/annotation.wrapper';
import { type ManualRedactionEntryType } from '@models/file/manual-redaction-entry.wrapper';
import type {
    DictionaryActions,
    IAddRedactionRequest,
    ILegalBasisChangeRequest,
    IManualAddResponse,
    IRecategorizationRequest,
    IRemoveRedactionRequest,
    IResizeRequest,
    ManualRedactionActions,
} from '@red/domain';
import { dictionaryActionsTranslations, manualRedactionActionsTranslations } from '@translations/annotation-actions-translations';
import { Roles } from '@users/roles';
import { NGXLogger } from 'ngx-logger';
import { EMPTY, of, pipe } from 'rxjs';
import { catchError, tap } from 'rxjs/operators';

function getResponseType(error: boolean, isConflict: boolean) {
    const isConflictError = isConflict ? 'conflictError' : 'error';
    return error ? isConflictError : 'success';
}

function getDictionaryMessage(action: DictionaryActions, error = false, isConflict = false): string {
    return dictionaryActionsTranslations[action][getResponseType(error, isConflict)];
}

function getMessage(action: ManualRedactionActions, isDictionary = false, error = false, isConflict = false): string {
    const translations = isDictionary ? dictionaryActionsTranslations : manualRedactionActionsTranslations;
    return translations[action][getResponseType(error, isConflict)];
}

@Injectable()
export class ManualRedactionService extends GenericService<IManualAddResponse> {
    protected readonly _defaultModelPath = 'manualRedaction';
    readonly #bulkRedaction = `${this._defaultModelPath}/bulk/redaction`;

    constructor(
        private readonly _toaster: Toaster,
        private readonly _logger: NGXLogger,
        private readonly _iqserPermissionsService: IqserPermissionsService,
    ) {
        super();
    }

    addRecommendation(annotations: AnnotationWrapper[], redaction: IAddRedactionRequest, dossierId: string, fileId: string) {
        const recommendations: List<IAddRedactionRequest> = annotations.map(annotation => ({
            addToDictionary: redaction.addToDictionary,
            addToAllDossiers: redaction.addToAllDossiers,
            sourceId: annotation.id,
            value: annotation.value,
            reason: annotation.legalBasis ?? 'Dictionary Request',
            positions: annotation.positions,
            type: redaction.type ?? annotation.type,
            comment: redaction.comment,
        }));
        return this.addAnnotation(recommendations, dossierId, fileId);
    }

    recategorizeRedactions(
        body: List<IRecategorizationRequest>,
        dossierId: string,
        fileId: string,
        successMessageParameters?: {
            [key: string]: string;
        },
        includeUnprocessed = false,
    ) {
        return this.recategorize(body, dossierId, fileId, includeUnprocessed).pipe(
            this.#showToast('recategorize-annotation', false, successMessageParameters),
        );
    }

    addAnnotation(
        requests: List<IAddRedactionRequest>,
        dossierId: string,
        fileId: string,
        options?: { hint?: boolean; dictionaryLabel?: string },
    ) {
        const toast = requests[0].addToDictionary
            ? this.#showAddToDictionaryToast(requests, options?.dictionaryLabel)
            : this.#showToast(options?.hint ? 'force-hint' : 'add');
        const canAddRedaction = this._iqserPermissionsService.has(Roles.redactions.write);
        if (canAddRedaction) {
            return this.add(requests, dossierId, fileId).pipe(toast);
        }

        return of(undefined);
    }

    bulkForce(requests: List<ILegalBasisChangeRequest>, dossierId: string, fileId: string, isIgnoredHint = false) {
        return this.forceRedaction(requests, dossierId, fileId).pipe(this.#showToast(!isIgnoredHint ? 'force-redaction' : 'force-hint'));
    }

    undoRequest(annotationIds: List, dossierId: string, fileId: string, modifyDictionary = false) {
        return this.undo(annotationIds, dossierId, fileId).pipe(this.#showToast('undo', modifyDictionary));
    }

    removeRedaction(
        body: List<IRemoveRedactionRequest>,
        dossierId: string,
        fileId: string,
        removeFromDictionary = false,
        isHint = false,
        includeUnprocessed = false,
    ) {
        return this.remove(body, dossierId, fileId, includeUnprocessed).pipe(
            this.#showToast(!isHint ? 'remove' : 'remove-hint', removeFromDictionary),
        );
    }

    getTitle(type: ManualRedactionEntryType) {
        switch (type) {
            case 'REDACT':
                return _('manual-annotation.dialog.header.redact');
            case 'HINT':
                return _('manual-annotation.dialog.header.hint');
            case 'FALSE_POSITIVE':
                return _('manual-annotation.dialog.header.false-positive');
            case 'REDACTION':
                return _('manual-annotation.dialog.header.redaction');
        }
    }

    add(body: List<IAddRedactionRequest>, dossierId: string, fileId: string) {
        return this._post(body, `${this.#bulkRedaction}/add/${dossierId}/${fileId}`).pipe(this.#log('Add', body));
    }

    recategorize(body: List<IRecategorizationRequest>, dossierId: string, fileId: string, includeUnprocessed = false) {
        return this._post(body, `${this.#bulkRedaction}/recategorize/${dossierId}/${fileId}?includeUnprocessed=${includeUnprocessed}`).pipe(
            this.#log('Recategorize', body),
        );
    }

    undo(annotationIds: List, dossierId: string, fileId: string) {
        const url = `${this._defaultModelPath}/bulk/undo/${dossierId}/${fileId}`;
        return super.delete(annotationIds, url).pipe(this.#log('Undo', annotationIds));
    }

    remove(body: List<IRemoveRedactionRequest>, dossierId: string, fileId: string, includeUnprocessed = false) {
        return this._post(body, `${this.#bulkRedaction}/remove/${dossierId}/${fileId}?includeUnprocessed=${includeUnprocessed}`).pipe(
            this.#log('Remove', body),
        );
    }

    forceRedaction(body: List<ILegalBasisChangeRequest>, dossierId: string, fileId: string) {
        return this._post(body, `${this.#bulkRedaction}/force/${dossierId}/${fileId}`).pipe(this.#log('Force redaction', body));
    }

    resize(body: List<IResizeRequest>, dossierId: string, fileId: string, includeUnprocessed = false) {
        return this._post(body, `${this.#bulkRedaction}/resize/${dossierId}/${fileId}?includeUnprocessed=${includeUnprocessed}`).pipe(
            this.#log('Resize', body),
        );
    }

    #log(action: string, body: unknown) {
        return tap(response => {
            this._logger.info(`[MANUAL-REDACTIONS] ${action} `, body, response);
        });
    }

    #showToast(
        action: ManualRedactionActions | DictionaryActions,
        isDictionary = false,
        successMessageParameters?: { [key: string]: string },
    ) {
        return pipe(
            catchError((error: unknown) => {
                const isConflict = (error as HttpErrorResponse).status === HttpStatusCode.Conflict;
                this._toaster.error(getMessage(action, isDictionary, true, isConflict), {
                    error: error as HttpErrorResponse,
                    positionClass: 'toast-file-preview',
                });
                return EMPTY;
            }),
            tap(() =>
                this._toaster.success(getMessage(action, isDictionary), {
                    params: successMessageParameters,
                    positionClass: 'toast-file-preview',
                }),
            ),
        );
    }

    #showAddToDictionaryToast(body: List<IAddRedactionRequest>, dictionaryLabel?: string) {
        return pipe(
            catchError((error: unknown) => {
                const isConflict = (error as HttpErrorResponse).status === HttpStatusCode.Conflict;
                this._toaster.error(getDictionaryMessage('add', true, isConflict), {
                    error: error as HttpErrorResponse,
                    params: {
                        dictionaryName: dictionaryLabel,
                        content: body[0].value,
                    },
                    positionClass: 'toast-file-preview',
                });
                return EMPTY;
            }),
            tap(() => this._toaster.success(getDictionaryMessage('add'), { positionClass: 'toast-file-preview' })),
        );
    }
}
