import { Observable } from 'rxjs';
import { take, concatMap, map } from 'rxjs/operators';

import { Injectable, Inject } from '@angular/core';

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

import { LocaleCode } from '@ucap/core';

import { PasswordUtil } from '@ucap/pi';
import { LoginResponse, SSOMode } from '@ucap/protocol-authentication';
import { NativeService } from '@ucap/native';

import {
  SessionStorageService,
  LocalStorageService
} from '@ucap/ng-web-storage';
import { UCAP_NATIVE_SERVICE } from '@ucap/ng-native';
import { InnerProtocolService } from '@ucap/ng-protocol-inner';
import { AuthenticationProtocolService } from '@ucap/ng-protocol-authentication';
import { LoginActions } from '@ucap/ng-store-authentication';

import { LoginSession } from '@app/models/login-session';
import { UserStore } from '@app/models/user-store';

import { AppKey } from '@app/types/app-key.type';

import { environment } from '@environments';

@Injectable({
  providedIn: 'root'
})
export class AppAuthenticationService {
  constructor(
    private innerProtocolService: InnerProtocolService,
    private authenticationProtocolService: AuthenticationProtocolService,
    private sessionStorageService: SessionStorageService,
    private localStorageService: LocalStorageService,
    private store: Store<any>,
    @Inject(UCAP_NATIVE_SERVICE) private nativeService: NativeService
  ) {}

  getUserStore(): UserStore {
    return this.localStorageService.encGet<UserStore>(
      AppKey.UserStore,
      environment.productConfig.localEncriptionKey
    );
  }

  getUserStore$(): Observable<UserStore> {
    return this.localStorageService.encGet$<UserStore>(
      AppKey.UserStore,
      environment.productConfig.localEncriptionKey
    );
  }

  setUserStore(userStore: UserStore) {
    const oldUserStore = this.getUserStore();

    this.localStorageService.encSet<UserStore>(
      AppKey.UserStore,
      {
        ...oldUserStore,
        ...userStore
      },
      environment.productConfig.localEncriptionKey
    );
  }

  getLoginSession(): LoginSession {
    return this.sessionStorageService.get<LoginSession>(AppKey.LoginSession);
  }

  getLoginSession$(): Observable<LoginSession> {
    return this.sessionStorageService.get$<LoginSession>(AppKey.LoginSession);
  }

  setLoginSession(loginSession: LoginSession) {
    const oldLoginSession = this.getLoginSession();
    this.sessionStorageService.set<LoginSession>(AppKey.LoginSession, {
      ...oldLoginSession,
      ...loginSession
    });
  }

  loggedIn(): boolean {
    const loginSession = this.getLoginSession();
    return !!loginSession && !!loginSession.loginId;
  }

  loggedIn$(): Observable<boolean> {
    return this.getLoginSession$().pipe(
      map((loginSession) => !!loginSession && !!loginSession.loginId)
    );
  }

  useAutoLogin(): UserStore {
    const userStore = this.getUserStore();
    return !!userStore &&
      !!userStore.settings &&
      !!userStore.settings.general &&
      userStore.settings.general.autoLogin
      ? userStore
      : null;
  }

  useAutoLogin$(): Observable<UserStore> {
    return this.getUserStore$().pipe(
      map((userStore) =>
        !!userStore &&
        !!userStore.settings &&
        !!userStore.settings.general &&
        userStore.settings.general.autoLogin
          ? userStore
          : null
      )
    );
  }

  async postWebLogin(
    loginSession: LoginSession,
    rememberMe: boolean,
    autoLogin: boolean
  ) {
    const prevLoginSession = this.getLoginSession();

    const newLoginSession = {
      ...prevLoginSession,
      ...loginSession,
      localeCode: LocaleCode.Korean
    };
    const encLoginPw = PasswordUtil.encrypt(newLoginSession.loginPw);

    this.setLoginSession({
      ...newLoginSession,
      initPw: newLoginSession.loginId === newLoginSession.loginPw,
      loginPw: encLoginPw
    });

    let userStore = this.getUserStore();

    if (!userStore) {
      userStore = {
        idleCheckTime: 10,
        settings: {
          ...environment.productConfig.defaultSettings,
          chat: {
            ...environment.productConfig.defaultSettings.chat,
            downloadPath: await this.nativeService.getPath(
              'documents',
              environment.productConfig.file.defaultDownloadFolder
            )
          }
        }
      };

      if (!!environment.productConfig.defaultSettings.general.autoLaunch) {
        this.nativeService.changeAutoLaunch(
          environment.productConfig.defaultSettings.general.autoLaunch
        );
      }
    }

    userStore = {
      ...userStore,
      companyGroupType: loginSession.companyGroupType,
      companyCode: loginSession.companyCode,
      loginId: loginSession.loginId,
      loginPw: loginSession.loginPw
    };

    if (rememberMe || autoLogin) {
      userStore = {
        ...userStore,
        rememberMe
      };
      userStore.settings.general.autoLogin = autoLogin;
    }

    this.setUserStore(userStore);

    this.sessionStorageService.remove(AppKey.LogoutSession);
  }

  createSession(): Promise<{
    loginRes: LoginResponse;
    loginSession: LoginSession;
  }> {
    return new Promise<{
      loginRes: LoginResponse;
      loginSession: LoginSession;
    }>(async (resolve, reject) => {
      const loginSession = this.getLoginSession();

      const networkInfo = await this.nativeService.getNetworkInfo();
      const localIp =
        !!networkInfo && 0 < networkInfo.length && !!networkInfo[0].ip
          ? networkInfo[0].ip
          : '';
      const localMac =
        !!networkInfo && 0 < networkInfo.length && !!networkInfo[0].mac
          ? networkInfo[0].mac
          : '';

      this.innerProtocolService
        .conn({})
        .pipe(
          take(1),
          concatMap((connRes) => {
            return this.authenticationProtocolService
              .login({
                loginId: loginSession.loginId,
                loginPw: loginSession.loginPw,
                deviceType: loginSession.deviceType,
                deviceId: ' ',
                token: '',
                localeCode: loginSession.localeCode,
                pushId: ' ',
                companyCode: loginSession.companyCode,
                passwordEncodingType: 1,
                clientVersion: '',
                reconnect: false,
                ip: localIp,
                hostName: localMac,
                ssoMode: SSOMode.AUTH,
                userSpecificInformation: 'PRO_000482',
                andId: '',
                andPushRefreshYn: ''
              })
              .pipe(take(1));
          })
        )
        .subscribe(
          (loginRes) => {
            this.store.dispatch(
              LoginActions.loginSuccess({
                res: loginRes,
                loginSession
              })
            );
            resolve({
              loginSession,
              loginRes
            });
          },
          (error) => {
            reject(error);
          }
        );
    });
  }

  logout() {
    this.sessionStorageService.remove(AppKey.LoginResponse);
    this.sessionStorageService.remove(AppKey.VerInfoResponse);
    this.sessionStorageService.remove(AppKey.LoginSession);
    this.sessionStorageService.remove(AppKey.UrlInfoResponse);
    this.sessionStorageService.remove(AppKey.AuthResponse);

    let userStore = this.getUserStore();
    if (!!userStore) {
      userStore = {
        ...userStore,
        settings: {
          ...userStore.settings,
          general: {
            ...userStore.settings.general,
            autoLogin: false
          }
        }
      };

      this.setUserStore(userStore);
    }
  }
}
