import { Observable, forkJoin, Subject } from 'rxjs';
import { take, takeUntil } from 'rxjs/operators';

import { Injectable } from '@angular/core';
import {
  Resolve,
  ActivatedRouteSnapshot,
  RouterStateSnapshot
} from '@angular/router';

import { Store } from '@ngrx/store';

import { StatusCode } from '@ucap/api';

import { LogService } from '@ucap/ng-logger';
import { PublicApiService } from '@ucap/ng-api-public';
import { ExternalApiService } from '@ucap/ng-api-external';
import { ProtocolService } from '@ucap/ng-protocol';

import { CompanyActions } from '@ucap/ng-store-organization';
import { ConfigurationActions } from '@ucap/ng-store-authentication';

import { AppAuthenticationService } from '@app/services/app-authentication.service';
import { LoginSession } from '@app/models/login-session';

@Injectable()
export class AppSessionResolver implements Resolve<void> {
  constructor(
    private publicApiService: PublicApiService,
    private externalApiService: ExternalApiService,
    private protocolService: ProtocolService,
    private store: Store<any>,
    private appAuthenticationService: AppAuthenticationService,
    private logService: LogService
  ) {}

  resolve(
    route: ActivatedRouteSnapshot,
    state: RouterStateSnapshot
  ): void | Observable<void> | Promise<void> {
    return new Promise<void>(async (resolve, reject) => {
      try {
        const loginSession = this.appAuthenticationService.getLoginSession();
        if (loginSession.alive) {
          resolve();
          return;
        }

        this.store.dispatch(
          CompanyActions.companies({
            req: { companyGroupCode: loginSession.companyGroupCode }
          })
        );

        forkJoin([
          this.publicApiService
            .versionInfo2({
              deviceType: loginSession.deviceType,
              desktopType: loginSession.desktopType,
              companyGroupType: loginSession.companyGroupType,
              companyCode: loginSession.companyCode,
              loginId: loginSession.loginId
            })
            .pipe(take(1)),
          this.externalApiService
            .urlInfo({
              deviceType: loginSession.deviceType,
              loginId: loginSession.loginId
            })
            .pipe(take(1))
        ])
          .pipe(take(1))
          .subscribe(
            ([versionInfo2Res, urlInfoRes]) => {
              if (
                StatusCode.Fail === versionInfo2Res.statusCode ||
                StatusCode.Fail === urlInfoRes.statusCode
              ) {
                if (StatusCode.Fail === versionInfo2Res.statusCode) {
                  this.store.dispatch(
                    ConfigurationActions.versionInfo2Failure({
                      error: versionInfo2Res.errorMessage
                    })
                  );

                  reject(versionInfo2Res.errorMessage);
                }

                if (StatusCode.Fail === urlInfoRes.statusCode) {
                  this.store.dispatch(
                    ConfigurationActions.urlInfoFailure({
                      error: urlInfoRes.errorMessage
                    })
                  );

                  reject(urlInfoRes.errorMessage);
                }

                return;
              }

              this.store.dispatch(
                ConfigurationActions.versionInfo2Success({
                  res: versionInfo2Res
                })
              );

              this.store.dispatch(
                ConfigurationActions.urlInfoSuccess({
                  res: urlInfoRes
                })
              );

              const destroySubject = new Subject<boolean>();
              this.appAuthenticationService
                .getLoginSession$()
                .pipe(takeUntil(destroySubject))
                .subscribe(
                  (v) => {
                    if ((v as LoginSession).alive) {
                      destroySubject.next(true);
                      destroySubject.complete();
                      resolve();
                    }
                  },
                  (error) => {
                    reject(error);
                  }
                );

              this.protocolService.connect(versionInfo2Res.serverIp);
            },
            (error) => {
              reject(error);
            }
          );
      } catch (error) {
        reject(error);
      }
    });
  }
}
