// @ts-strict-ignore
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, HostListener, inject, OnDestroy, OnInit, signal, } from "@angular/core";
import { FormGroup, ReactiveFormsModule } from "@angular/forms";
import { DomSanitizer } from "@angular/platform-browser";
import { ActivatedRoute, Router, RouterModule } from "@angular/router";
import { ModalController } from "@ionic/angular";
import { TranslateService } from "@ngx-translate/core";
import { NgxSpinnerComponent } from "ngx-spinner";
import { Subject } from "rxjs";
import { filter, takeUntil } from "rxjs/operators";
import { NavigationBackButtonComponent } from "src/app/shared/components/navigation/back-button/back-button";
import { NavigationService } from "src/app/shared/components/navigation/service/navigation.service";
import { ComponentJsonApiRequest } from "src/app/shared/jsonrpc/request/componentJsonApiRequest";
import { PipeComponentsModule } from "src/app/shared/pipe/pipe.module";
import { RouteService } from "src/app/shared/service/route.service";
import { environment } from "src/environments";
import { CommonUiModule } from "../../../shared/common-ui.module";
import { Edge, Service, Utils, Websocket } from "../../../shared/shared";
import { InstallAppComponent } from "./install.component";
import { Flags } from "./jsonrpc/flag/flags";
import { GetApp } from "./jsonrpc/getApp";
import { GetAppDescriptor } from "./jsonrpc/getAppDescriptor";
import { GetApps } from "./jsonrpc/getApps";
import { AppCenter } from "./keypopup/appCenter";
import { AppCenterGetPossibleApps } from "./keypopup/appCenterGetPossibleApps";
import { AppCenterIsAppFree } from "./keypopup/appCenterIsAppFree";
import { KeyModalComponent, KeyValidationBehaviour } from "./keypopup/modal.component";
import { canEnterKey, hasPredefinedKey } from "./permissions";

@Component({
    selector: SingleAppComponent.SELECTOR,
    templateUrl: "./single.component.html",
    standalone: true,
    imports: [
        CommonUiModule,
        PipeComponentsModule,
        NgxSpinnerComponent,
        ReactiveFormsModule,
        RouterModule,
        NavigationBackButtonComponent,
    ],
    changeDetection: ChangeDetectionStrategy.OnPush, // ChangeDetectionStrategy.Default
})
export class SingleAppComponent implements OnInit, OnDestroy {
    private static readonly SELECTOR = "app-single";
    public readonly spinnerId: string = SingleAppComponent.SELECTOR;

    public form = signal<FormGroup | null>(null);
    public model: any | null = null;

    protected canEnterKey: boolean | undefined;
    protected hasPredefinedKey: boolean | undefined;
    protected keyForFreeApps: string;
    protected isFreeApp: boolean = false;
    protected isPreInstalledApp: boolean = false;

    private appId: string | null = null;
    private appName: string | null = null;
    private app: GetApps.App | null = null;
    private descriptor: GetAppDescriptor.AppDescriptor | null = null;
    private isXL: boolean = true;
    // for stopping spinner when all responses are recieved
    private readonly requestCount: number = 3;
    private receivedResponse: number = 0;
    private edge: Edge | null = null;
    private key: string | null = null;
    private useMasterKey: boolean = false;
    private stopOnDestroy: Subject<void> = new Subject<void>();

    private readonly routeService: RouteService = inject(RouteService);
    private readonly navigationService: NavigationService = inject(NavigationService);
    private readonly cdRef: ChangeDetectorRef = inject(ChangeDetectorRef);

    public constructor(
        private route: ActivatedRoute,
        private router: Router,
        protected utils: Utils,
        private websocket: Websocket,
        private translate: TranslateService,
        private service: Service,
        private sanitizer: DomSanitizer,
        protected modalController: ModalController,
    ) {}

    @HostListener("window:resize", ["$event"])
    protected onResize(event) {
        this.updateIsXL();
    }

    public ngOnInit() {
        this.service.startSpinner(this.spinnerId);
        this.updateIsXL();

        this.appId = this.routeService.getQueryParam("appId");
        this.appName = this.routeService.getQueryParam("name");
        const appId = this.appId;
        this.service.getCurrentEdge().then((edge) => {
            this.edge = edge;

            this.edge
                .sendRequest(
                    this.websocket,
                    new AppCenter.Request({
                        payload: new AppCenterIsAppFree.Request({
                            appId: this.appId,
                        }),
                    }),
                )
                .then((response) => {
                    const result = (response as AppCenterIsAppFree.Response).result;
                    this.isFreeApp = result.isAppFree;
                })
                .catch(() => {
                    this.isFreeApp = false;
                });

            // update if the app is free depending of the configured key in the edge config
            this.edge
                .getConfig(this.websocket)
                .pipe(
                    filter((config) => config !== null),
                    takeUntil(this.stopOnDestroy),
                )
                .subscribe((next) => {
                    const appManager = next.getComponent("_appManager");
                    const newKeyForFreeApps = appManager.properties["keyForFreeApps"];
                    if (!newKeyForFreeApps) {
                        // no key in config
                        this.increaseReceivedResponse();
                    }
                    if (this.keyForFreeApps === newKeyForFreeApps) {
                        return;
                    }
                    this.keyForFreeApps = newKeyForFreeApps;
                    // update free apps
                    this.edge
                        .sendRequest(
                            this.websocket,
                            new AppCenter.Request({
                                payload: new AppCenterGetPossibleApps.Request({
                                    key: this.keyForFreeApps,
                                }),
                            }),
                        )
                        .then((response) => {
                            const result = (response as AppCenterGetPossibleApps.Response).result;
                            this.isPreInstalledApp = result.bundles.some((bundle) => {
                                return bundle.some((app) => {
                                    return app.appId == this.appId;
                                });
                            });
                        })
                        .finally(() => {
                            this.increaseReceivedResponse();
                        });
                });

            this.service.metadata.pipe(takeUntil(this.stopOnDestroy)).subscribe((entry) => {
                this.canEnterKey = canEnterKey(edge, entry.user);
                this.hasPredefinedKey = hasPredefinedKey(edge, entry.user);
            });

            // set appname, image ...
            const state = history?.state;
            if (state && "app" in history.state) {
                if ("app" in history.state) {
                    this.setApp(history.state.app);
                }
                if ("appKey" in history.state) {
                    this.key = history.state.appKey;
                }
                if ("useMasterKey" in history.state) {
                    this.useMasterKey = history.state.useMasterKey;
                }
            } else {
                edge.sendRequest(
                    this.websocket,
                    new ComponentJsonApiRequest({
                        componentId: "_appManager",
                        payload: new GetApp.Request({ appId: appId }),
                    }),
                )
                    .then((response) => {
                        const app = (response as GetApp.Response).result.app;
                        app.imageUrl = environment.links.APP_CENTER.APP_IMAGE(
                            this.translate.getCurrentLang(),
                            app.appId,
                        );
                        this.setApp(app);
                    })
                    .catch((reason) => {
                        console.error(reason.error);
                        this.service.toast(
                            "Error while receiving App[" + appId + "]: " + reason.error.message,
                            "danger",
                        );
                    });
            }
            // set app descriptor
            edge.sendRequest(
                this.websocket,
                new ComponentJsonApiRequest({
                    componentId: "_appManager",
                    payload: new GetAppDescriptor.Request({ appId: appId }),
                }),
            )
                .then((response) => {
                    const descriptor = (response as GetAppDescriptor.Response).result;
                    this.descriptor = GetAppDescriptor.postprocess(descriptor, this.sanitizer);
                })
                .catch(
                    InstallAppComponent.errorToast(
                        this.service,
                        (error) => "Error while receiving AppDescriptor for App[" + appId + "]: " + error,
                    ),
                )
                .finally(() => {
                    this.increaseReceivedResponse();
                    this.cdRef.markForCheck();
                });
        });
    }

    public ngOnDestroy(): void {
        this.stopOnDestroy.next();
        this.stopOnDestroy.complete();
    }

    protected iFrameStyle() {
        const styles = {
            height: this.isXL ? "100%" : window.innerHeight + "px",
        };
        return styles;
    }

    protected installApp(appId: string) {
        if (this.key || this.useMasterKey || Flags.getByType(this.app.flags, Flags.FREE_FROM_DEPENDENCY)) {
            // if key already set navigate directly to installation view
            const state = this.useMasterKey ? { useMasterKey: true } : { appKey: this.key };
            this.router.navigate(["./install"], {
                queryParams: { name: this.appName, appId: this.appId },
                state: state,
                relativeTo: this.route,
            });
            return;
        }
        if (this.isFreeApp) {
            this.router.navigate(["./install"], {
                queryParams: { name: this.appName, appId: this.appId },
                relativeTo: this.route,
            });
            return;
        }
        // show modal to let the user enter a key
        this.presentModal(appId, KeyValidationBehaviour.NAVIGATE);
    }

    protected registerKey(appId: string) {
        this.presentModal(appId, KeyValidationBehaviour.REGISTER);
    }

    private updateIsXL() {
        this.isXL = 1200 <= window.innerWidth;
    }

    private setApp(app: GetApps.App) {
        this.app = app;
        this.form.set(new FormGroup({}));
        this.increaseReceivedResponse();
    }

    private increaseReceivedResponse() {
        this.receivedResponse++;
        if (this.receivedResponse == this.requestCount) {
            this.receivedResponse = 0;
            this.service.stopSpinner(this.spinnerId);
        }
    }

    // popup for key
    private async presentModal(appId: string, behaviour: KeyValidationBehaviour) {
        const modal = await this.modalController.create({
            component: KeyModalComponent,
            componentProps: {
                edge: this.edge,
                appId: appId,
                behaviour: behaviour,
                appName: this.appName,
                route: this.route,
            },
            cssClass: "auto-height",
        });
        return await modal.present();
    }
}
