import { ChangeDetectorRef, Component, ElementRef, inject, OnInit, ViewChild } from '@angular/core';
import WebViewer, { WebViewerInstance } from '@pdftron/webviewer';
import { HttpClient } from '@angular/common/http';
import { FormBuilder, FormGroup } from '@angular/forms';
import { getConfig, IconButtonTypes, IqserPermissionsService, LoadingService, Toaster } from '@iqser/common-ui';
import {
    AppConfig,
    DOSSIER_TEMPLATE_ID,
    type IWatermark,
    type User,
    WATERMARK_HORIZONTAL_ALIGNMENTS,
    WATERMARK_ID,
    WATERMARK_VERTICAL_ALIGNMENTS,
    WatermarkAlignment,
    WatermarkHorizontalAlignment,
    WatermarkOrientation,
    WatermarkOrientations,
    WatermarkVerticalAlignment,
} from '@red/domain';
import { stampPDFPage } from '@utils/page-stamper';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { WatermarkService } from '@services/entity-services/watermark.service';
import { BehaviorSubject, firstValueFrom, Observable, of } from 'rxjs';
import { LicenseService } from '@services/license.service';
import { UserPreferenceService } from '@users/user-preference.service';
import { Router } from '@angular/router';
import { WatermarksMapService } from '@services/entity-services/watermarks-map.service';
import { Roles } from '@users/roles';
import { environment } from '@environments/environment';
import { tap } from 'rxjs/operators';
import { watermarkTranslations } from '@translations/watermark-translations';
import { getCurrentUser } from '@iqser/common-ui/lib/users';
import { AsControl, BASE_HREF_FN, Debounce, getParam, trackByFactory } from '@iqser/common-ui/lib/utils';
import { TenantsService } from '@iqser/common-ui/lib/tenants';

export const DEFAULT_WATERMARK: Partial<IWatermark> = {
    text: 'Watermark',
    name: '',
    hexColor: '#000000',
    opacity: 30,
    fontSize: 40,
    fontType: 'helvetica',
    orientation: WatermarkOrientations.HORIZONTAL,
    horizontalTextAlignment: WATERMARK_HORIZONTAL_ALIGNMENTS.CENTER,
    verticalTextAlignment: WATERMARK_VERTICAL_ALIGNMENTS.CENTER,
} as const;

interface WatermarkForm {
    name: string;
    text: string;
    hexColor: string;
    opacity: number;
    fontSize: number;
    fontType: string;
    orientation: WatermarkOrientation;
    horizontalTextAlignment: WatermarkHorizontalAlignment;
    verticalTextAlignment: WatermarkVerticalAlignment;
}

@Component({
    templateUrl: './watermark-screen.component.html',
    styleUrls: ['./watermark-screen.component.scss'],
})
export class WatermarkScreenComponent implements OnInit {
    @ViewChild('viewer', { static: true }) private readonly _viewer: ElementRef<HTMLDivElement>;
    private readonly _convertPath = inject(BASE_HREF_FN);
    readonly #loaded$ = new BehaviorSubject(false);
    readonly #dossierTemplateId = getParam(DOSSIER_TEMPLATE_ID);
    readonly #watermarkId = Number(getParam(WATERMARK_ID));
    readonly #config = getConfig<AppConfig>();
    #watermark: Partial<IWatermark> = {};
    readonly iconButtonTypes = IconButtonTypes;
    readonly translations = watermarkTranslations;
    readonly trackBy = trackByFactory();
    readonly currentUser = getCurrentUser<User>();
    readonly form = this.#form;
    readonly watermark$: Observable<Partial<IWatermark>>;
    readonly fontOptions = [
        { value: 'times-new-roman', display: 'Times' },
        { value: 'helvetica', display: 'Helvetica' },
        { value: 'courier', display: 'Courier' },
    ];
    readonly orientationOptions = ['DIAGONAL', 'HORIZONTAL', 'VERTICAL'];
    instance: WebViewerInstance;
    readonly loaded$: Observable<boolean>;
    readonly watermarkHorizontalAlignments = Object.values(WATERMARK_HORIZONTAL_ALIGNMENTS);
    readonly watermarkVerticalAlignments = Object.values(WATERMARK_VERTICAL_ALIGNMENTS);
    currentAlignment: WatermarkAlignment;

    constructor(
        private readonly _http: HttpClient,
        private readonly _toaster: Toaster,
        private readonly _formBuilder: FormBuilder,
        readonly permissionsService: IqserPermissionsService,
        private readonly _loadingService: LoadingService,
        private readonly _tenantsService: TenantsService,
        private readonly _licenseService: LicenseService,
        private readonly _watermarkService: WatermarkService,
        private readonly _userPreferenceService: UserPreferenceService,
        private readonly _router: Router,
        private readonly _changeDetectorRef: ChangeDetectorRef,
        watermarksMapService: WatermarksMapService,
    ) {
        this.loaded$ = this.#loaded$.asObservable().pipe(tap(() => setTimeout(() => this._changeDetectorRef.detectChanges())));
        const watermark$ = watermarksMapService.watch$(this.#dossierTemplateId, this.#watermarkId);
        const obs$: Observable<Partial<IWatermark>> = this.#watermarkId ? watermark$ : of(DEFAULT_WATERMARK);
        this.watermark$ = obs$.pipe(tap(watermark => this.#initForm(watermark)));
    }

    get changed(): boolean {
        for (const key of Object.keys(this.form.getRawValue())) {
            if (this.#watermark[key] !== this.form.get(key)?.value) {
                return true;
            }
        }
        return false;
    }

    get valid(): boolean {
        if (!this.form.controls.name?.value || !this.form.controls.text?.value) {
            return false;
        }
        return this.form.valid;
    }

    get #form() {
        const form: FormGroup<AsControl<WatermarkForm>> = this._formBuilder.group({
            name: [null],
            text: [null],
            hexColor: [null],
            opacity: [null],
            fontSize: [null],
            fontType: [null],
            orientation: [null],
            horizontalTextAlignment: [null],
            verticalTextAlignment: [null],
        });

        if (!this.currentUser.isAdmin || !this.permissionsService.has(Roles.watermarks.write)) {
            form.disable();
        }

        return form;
    }

    @Debounce()
    async configChanged() {
        await this.#drawWatermark();
    }

    async ngOnInit() {
        await this.#loadViewer();
    }

    async save(): Promise<void> {
        const watermark: IWatermark = {
            id: this.#watermark.id,
            enabled: this.#watermark.id ? this.#watermark.enabled : true,
            dossierTemplateId: this.#dossierTemplateId,
            ...this.form.getRawValue(),
        };

        this._loadingService.start();

        try {
            const updatedWatermark = await this._watermarkService.saveWatermark(watermark);
            this._toaster.success(
                watermark.id ? _('watermark-screen.action.change-success') : _('watermark-screen.action.created-success'),
            );
            if (!watermark.id) {
                await this._router.navigate([
                    `/${this._tenantsService.activeTenantId}/main/admin/dossier-templates/${this.#dossierTemplateId}/watermarks/${
                        updatedWatermark.id
                    }`,
                ]);
            }
        } catch (error) {
            this._toaster.error(_('watermark-screen.action.error'));
        }

        this._loadingService.stop();
    }

    async revert() {
        this.form.patchValue({ ...this.#watermark });
        await this.configChanged();
    }

    async setValue(type: 'fontType' | 'orientation' | 'hexColor', value: any) {
        if (!this.form.get(type).disabled) {
            this.form.get(type).setValue(value);
            await this.configChanged();
        }
    }

    navigateTo($event: number) {
        this.instance.Core.documentViewer.displayPageLocation($event, 0, 0);
    }

    async alignHorizontally(alignment: WatermarkHorizontalAlignment) {
        this.form.controls['horizontalTextAlignment'].setValue(alignment);
        this.currentAlignment.horizontal = alignment;
        await this.configChanged();
    }

    async alignVertically(alignment: WatermarkVerticalAlignment) {
        this.form.controls['verticalTextAlignment'].setValue(alignment);
        this.currentAlignment.vertical = alignment;
        await this.configChanged();
    }

    async #initForm(watermark: Partial<IWatermark>) {
        this.#watermark = { ...watermark, dossierTemplateId: this.#dossierTemplateId };
        this.form.patchValue({ ...watermark });
        this.currentAlignment = {
            horizontal: this.#watermark.horizontalTextAlignment,
            vertical: this.#watermark.verticalTextAlignment,
        };
    }

    async #loadViewer() {
        this.instance = await WebViewer(
            {
                licenseKey: this._licenseService.activeLicenseKey,
                path: this._convertPath('/assets/wv-resources'),
                css: this._convertPath('/assets/pdftron/stylesheet.css'),
                fullAPI: true,
                isReadOnly: true,
                backendType: 'ems',
            },
            // use nativeElement instead of document.getElementById('viwer')
            // because WebViewer works better with this approach
            this._viewer.nativeElement,
        );

        this.instance.UI.setTheme(this._userPreferenceService.getTheme());

        this.instance.Core.documentViewer.addEventListener('documentLoaded', async () => {
            await this.#drawWatermark();
            this.#loaded$.next(true);
            this._loadingService.stop();
        });

        if (environment.production) {
            this.instance.Core.setCustomFontURL('https://' + window.location.host + this._convertPath('/assets/pdftron'));
        }

        this.#disableElements();
        await this.#loadDocument();
    }

    async #loadDocument() {
        const filename = `blank-${this.#config.WATERMARK_PREVIEW_PAPER_FORMAT || 'a4'}.pdf`;
        const request = this._http.get(`/assets/pdftron/${filename}`, {
            responseType: 'blob',
        });
        const blobData = await firstValueFrom(request);
        this.instance.UI.loadDocument(blobData, { filename });
    }

    #disableElements() {
        this.instance.UI.disableElements(['header', 'toolsHeader', 'pageNavOverlay', 'textPopup']);
    }

    async #drawWatermark() {
        const pdfNet = this.instance.Core.PDFNet;
        const document = await this.instance.Core.documentViewer.getDocument().getPDFDoc();

        await stampPDFPage(
            document,
            pdfNet,
            this.form.controls.text.value || '',
            this.form.controls.fontSize.value,
            this.form.controls.fontType.value,
            this.form.controls.orientation.value,
            this.form.controls.horizontalTextAlignment.value,
            this.form.controls.verticalTextAlignment.value,
            this.form.controls.opacity.value,
            this.form.controls.hexColor.value,
            [1, 2],
            this._licenseService.activeLicenseKey,
        );
        this.instance.Core.documentViewer.refreshAll();
        this.instance.Core.documentViewer.updateView([0, 1], 0);
    }
}
