import { Injectable, Inject, LOCALE_ID } from '@angular/core';
import { Router } from '@angular/router';
import { OAuthService,UserInfo } from 'angular-oauth2-oidc';
import { Store } from '@ngrx/store';
import { Actions,ofType,createEffect } from '@ngrx/effects';
import { of,from,zip } from 'rxjs';
import { withLatestFrom,mergeMap,switchMap,map,catchError,first} from 'rxjs/operators';
import * as appCommonActions from '../actions/app-common.actions';
import * as appCommonReducers from '../reducers/app-common.reducer';
import { ItemService } from '../services/item.service';
import { FolderService } from '../services/folder.service';
import { UserService } from '../services/user.service';
import { IItemTypes } from '../models/item.types';
import { IListItem } from '../models/list.item';
import {StateSerializerService} from '../services/state-serializer.service';

@Injectable()
export class AppCommonEffects {
  locale: string;

  login$ =  createEffect(() => this.actions$.pipe(
    ofType(appCommonActions.LOGIN),
    withLatestFrom(this.store$.select(appCommonReducers.selectGetInitialized)),
    mergeMap(([action, initialized]) => {
      const a = (action as appCommonActions.Login);
      this.oauthService$.initCodeFlow(a.url,{"prompt":"login"});
      return [];
    })),{dispatch:false});

    logout$ =  createEffect(() => this.actions$.pipe(
      ofType(appCommonActions.LOGOUT),
      mergeMap((action) => {
        this.oauthService$.revokeTokenAndLogout();
        return [];
      })),{dispatch:false});

  loadItemTypes$ =  createEffect(() => this.actions$.pipe(
    ofType(appCommonActions.LOADITEMTYPES),
    switchMap((action) => {
      return this.itemService$.getItemTypes().pipe(
        map((itemTypes: IItemTypes) => new appCommonActions.LoadItemTypesSuccess(itemTypes)),
        catchError(error => of(new appCommonActions.Fail(error))))
      }
  )));

  initUser$ =  createEffect(() => this.actions$.pipe(
    ofType(appCommonActions.INITUSER),
    first(),
    switchMap((action) => {
        return zip(this.userService$.getCurrentUser(),from(this.oauthService$.loadUserProfile())).pipe(
        switchMap(([user,userInfo]) => {
          if (location.hostname === 'localhost' || user.language === undefined || user.language === this.locale) 
          {
            return of(new appCommonActions.InitUserSuccess(user,userInfo as UserInfo))
          }
          return of(new appCommonActions.SwitchLanguage(user.language))
        }),
        catchError(error => of(new appCommonActions.Fail(error))))
    }
  )));

  switchLanguage$ = createEffect(() => this.actions$.pipe(
    ofType(appCommonActions.SWITCHLANGUAGE),
    switchMap((action) => {
      const a = action as appCommonActions.SwitchLanguage;
      if (location.href.includes(`/${this.locale}/`)) {
        const newPath = location.href.replace(`/${this.locale}/`, `/${a.locale}/`);
        location.replace(newPath);
      }
      return of(undefined);
    })),{ dispatch:false});

  initUserPackages$ =  createEffect(() => this.actions$.pipe(
    ofType(appCommonActions.INITUSERPACKAGES),
    switchMap(() => {
      return this.itemService$.getItemList('vnd.farmmaps.itemtype.package').pipe(
        switchMap((items) => of(new appCommonActions.InitUserPackagesSuccess(items))),
        catchError(error =>  of(new appCommonActions.Fail(error)))
      )
    })
  ));
  initPackages$ =  createEffect(() => this.actions$.pipe(
    ofType(appCommonActions.INITPACKAGES),
    switchMap(() => {
      return this.itemService$.getItemList('vnd.farmmaps.itemtype.package.template').pipe(
        switchMap((items) => of(new appCommonActions.InitPackagesSuccess(items))),
        catchError(error =>  of(new appCommonActions.Fail(error)))
      )
    })
  ));

  userPackagesChanged$ =  createEffect(() => this.actions$.pipe(
    ofType(appCommonActions.ITEMCHANGEDEVENT),
    switchMap((action) => {
      const a = action as appCommonActions.ItemChangedEvent;
      if(a.itemCode.endsWith(":USER_PACKAGES")) 
        return of(new appCommonActions.InitUserPackages());
      else
        return [];
    })
  ));
  
  initUserSettingsRoot$ =  createEffect(() => this.actions$.pipe(
    ofType(appCommonActions.INITUSERSETTINGSROOT),
    switchMap(_ => {
      return this.folderService$.getFolder('my_settings').pipe(
        switchMap((item) => of(new appCommonActions.InitUserSettingsRootSuccess(item))),
        catchError(error =>  of(new appCommonActions.Fail(error)))
      )
    })
  ));

  initUserSettingsRootChanged$ =  createEffect(() => this.actions$.pipe(
    ofType(appCommonActions.ITEMCHANGEDEVENT),
    switchMap((action) => {
      const a = action as appCommonActions.ItemChangedEvent;
      if(a.itemCode.endsWith(":USER_SETTINGS")) 
        return of(new appCommonActions.InitUserSettingsRoot());
      else
        return [];
    })
  ));

  initUserSuccess$ =  createEffect(() => this.actions$.pipe(
    ofType(appCommonActions.INITUSERSUCCESS),
    switchMap(() => {
      return [new appCommonActions.InitRoot(),new appCommonActions.InitUserPackages(),new appCommonActions.InitPackages(),new appCommonActions.InitUserSettingsRoot()];
    }
    )));

  initRoot$ =  createEffect(() => this.actions$.pipe(
    ofType(appCommonActions.INITROOT),
    switchMap(() => {
      return this.folderService$.getMyRoots().pipe(
        map((folders: IListItem[]) => new appCommonActions.InitRootSuccess(folders)),
        catchError(error => of(new appCommonActions.Fail(error))))
    }
    )));

  deleteItems$ =  createEffect(() => this.actions$.pipe(
    ofType(appCommonActions.DELETEITEMS),
    switchMap((action:appCommonActions.DeleteItems) => {
      return this.itemService$.deleteItems(action.itemCodes).pipe(
        map((deletedItemCodes: string[]) => new appCommonActions.DeleteItemsSuccess(deletedItemCodes)),
        catchError(error => of(new appCommonActions.Fail(error))))
    }
  )));

  editItem$ =  createEffect(() => this.actions$.pipe(
      ofType(appCommonActions.EDITITEM),
      withLatestFrom(this.store$.select(appCommonReducers.selectGetItemTypes)),
      switchMap(([action, itemtypes]) => {
        const a = action as appCommonActions.EditItem;
        var editor = "property";
        if(a.item.itemType) {
          const itemType = itemtypes[a.item.itemType];
          var editor = itemType && itemType.editor ? itemType.editor : editor;  
        }
        if (editor == 'croppingscheme') { 
          // Exeception: no 'item' in route
          this.router$.navigate(['/editor', editor, a.item.code])
        }
        else {
          this.router$.navigate(['/editor', editor, 'item', a.item.code])
        }
        return [];
      }
    )
  ));

  viewItem$ =  createEffect(() => this.actions$.pipe(
    ofType(appCommonActions.VIEWITEM),
    withLatestFrom(this.store$.select(appCommonReducers.selectGetItemTypes)),
    switchMap(([action, itemtypes]) => {
      const a = action as appCommonActions.ViewItem;
      const itemType = itemtypes[a.item.itemType];
      const viewer = itemType.viewer;
      const editor = itemType.editor;
      if(viewer == 'select_as_mapitem') {
        const queryState = {
          itemCode: a.item.code,
          parentCode: null,
          level: 1,
          itemType: null,
          bboxFilter: false,
          query: null,
          tags: null,
          endDate: null,
          startDate: null,
          bbox: []
        };
        const query = this.stateSerializerService$.serialize(queryState);
        this.router$.navigate(['/map', query ])
      }else if(viewer == 'edit_in_editor') {
        this.router$.navigate(['/editor', editor, 'item', a.item.code])
      } else if(viewer) {
        this.router$.navigate(['/viewer', viewer, 'item', a.item.code])
      }
      return [];
    }
    )));

 
  fail$ =  createEffect(() => this.actions$.pipe(
    ofType(appCommonActions.FAIL),
    map((action) => {
      const failAction = action as appCommonActions.Fail;
      //console.debug(failAction.payload)
      return null;
    })),{dispatch:false});

    online$ =  createEffect(() => this.actions$.pipe(
      ofType(appCommonActions.ONLINE),
      switchMap((action) => {
          //console.debug("Online: Check token");
          if(!this.oauthService$.hasValidAccessToken()) {
            //console.debug("No valid token, try to refresh");
            if(this.oauthService$.getRefreshToken() != null ) {
              //console.debug("We have a refresh token");
              this.oauthService$.refreshToken();              
            }
          }
          return of(undefined);
      })),{dispatch:false});



  constructor(private actions$: Actions, private store$: Store<appCommonReducers.State>, private oauthService$: OAuthService, private itemService$: ItemService, private folderService$:FolderService, private userService$: UserService, private router$: Router, private stateSerializerService$:StateSerializerService, @Inject(LOCALE_ID) locale: string) {
    this.locale = locale;
    store$.dispatch(new appCommonActions.LoadItemTypes());
  }
}