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,
    Dossier,
    IAddRedactionRequest,
    ILegalBasisChangeRequest,
    IManualAddResponse,
    IRecategorizationRequest,
    IRemoveRedactionRequest,
    IResizeRequest,
    ManualRedactionActions,
} from '@red/domain';
import { ActiveDossiersService } from '@services/dossiers/active-dossiers.service';
import { PermissionsService } from '@services/permissions.service';
import { dictionaryActionsTranslations, manualRedactionActionsTranslations } from '@translations/annotation-actions-translations';
import { Roles } from '@users/roles';
import { NGXLogger } from 'ngx-logger';
import { firstValueFrom, of } from 'rxjs';
import { 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 #bulkRequest = `${this._defaultModelPath}/bulk/request`;
    readonly #bulkRedaction = `${this._defaultModelPath}/bulk/redaction`;

    constructor(
        private readonly _toaster: Toaster,
        private readonly _logger: NGXLogger,
        private readonly _permissionsService: PermissionsService,
        private readonly _iqaerPermissionsService: IqserPermissionsService,
        private readonly _activeDossiersService: ActiveDossiersService,
    ) {
        super();
    }

    async addComment(comment: string, annotationId: string, dossierId: string, fileId: string) {
        const url = `${this._defaultModelPath}/comment/add/${dossierId}/${fileId}/${annotationId}`;
        const request = await firstValueFrom(this._post<{ commentId: string }>({ text: comment }, url));
        return request.commentId;
    }

    deleteComment(commentId: string, annotationId: string, dossierId: string, fileId: string) {
        const url = `${this._defaultModelPath}/comment/undo/${dossierId}/${fileId}/${annotationId}/${commentId}`;
        return firstValueFrom(super.delete({}, url));
    }

    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,
            comment: redaction.comment,
        }));
        return this.addAnnotation(recommendations, dossierId, fileId);
    }

    changeLegalBasis(body: List<ILegalBasisChangeRequest>, dossierId: string, fileId: string) {
        return this.legalBasisChange(body, dossierId, fileId).pipe(this.#showToast('change-legal-basis'));
    }

    recategorizeRedactions(body: List<IRecategorizationRequest>, dossierId: string, fileId: string) {
        return this.recategorize(body, dossierId, fileId).pipe(this.#showToast('change-type'));
    }

    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._iqaerPermissionsService.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));
    }

    declineOrRemove(annotationIds: List, dossierId: string, fileId: string, modifyDictionary = false) {
        if (this._permissionsService.isApprover(this.#dossier(dossierId))) {
            return this.decline(annotationIds, dossierId, fileId).pipe(this.#showToast('decline', modifyDictionary));
        }

        return this.undo(annotationIds, dossierId, fileId).pipe(this.#showToast('undo', modifyDictionary));
    }

    removeRedaction(body: List<IRemoveRedactionRequest>, dossierId: string, fileId: string, removeFromDictionary = false, isHint = false) {
        return this.remove(body, dossierId, fileId).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) {
        return this._post(body, `${this.#bulkRedaction}/recategorize/${dossierId}/${fileId}`).pipe(this.#log('Recategorize', body));
    }

    legalBasisChange(body: List<ILegalBasisChangeRequest>, dossierId: string, fileId: string) {
        return this._post(body, `${this.#bulkRedaction}/legalBasisChange/${dossierId}/${fileId}`).pipe(
            this.#log('Legal basis change', body),
        );
    }

    requestLegalBasisChange(body: List<ILegalBasisChangeRequest>, dossierId: string, fileId: string) {
        return this._post(body, `${this.#bulkRequest}/legalBasis/${dossierId}/${fileId}`).pipe(
            this.#log('Request legal basis change', body),
        );
    }

    approve(annotationIds: List, dossierId: string, fileId: string) {
        return this._post(annotationIds, `${this._defaultModelPath}/bulk/approve/${dossierId}/${fileId}`).pipe(
            this.#log('Approve', annotationIds),
            this.#showToast('approve'),
        );
    }

    decline(annotationIds: List, dossierId: string, fileId: string) {
        return this._post(annotationIds, `${this._defaultModelPath}/bulk/decline/${dossierId}/${fileId}`).pipe(
            this.#log('Decline', annotationIds),
        );
    }

    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) {
        return this._post(body, `${this.#bulkRedaction}/remove/${dossierId}/${fileId}`).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) {
        return this._post(body, `${this.#bulkRedaction}/resize/${dossierId}/${fileId}`).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) {
        return tap({
            next: () => this._toaster.success(getMessage(action, isDictionary), { positionClass: 'toast-file-preview' }),
            error: (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',
                });
            },
        });
    }

    #showAddToDictionaryToast(body: List<IAddRedactionRequest>, dictionaryLabel?: string) {
        return tap({
            next: () => this._toaster.success(getDictionaryMessage('add'), { positionClass: 'toast-file-preview' }),
            error: (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',
                });
            },
        });
    }

    #dossier(dossierId: string): Dossier {
        return this._activeDossiersService.find(dossierId);
    }
}
