import { Injectable } from '@angular/core';
import { createResourceGroupQuery, deleteResourceGroupQuery, resourceTreeQuery, editResourceGroupNameQuery, editResourceGroupOrderingQuery, editResourceGroupClosedQuery, editRoomPlacementQuery, editDialoguePlacementQuery } from './querys';
import { SubscriptionService } from './subscription.service';
import { ServerRestApiService } from '../services/server-rest-api.service';
import { ACCOUNT_CRYPTO_CONTEXT } from '../crypto/context/account/__init__';
import { AccountConfigDumpArgs, AccountConfigLoadArgs } from '../crypto/context/account/args';
import { AuthService } from './auth.service';
import { AccountService } from './account.service';
import { RoomService } from './room.service';
import { SubscriptionServiceEvent, SubscriptionServiceEventType } from './subscription-event';
import { WorkspaceQueryRecord } from './query-records/workspace-records';

@Injectable({
  providedIn: 'root'
})
export class ResourceService {

  private createResourceGroupHandlers:Function[] = [];
  private modifyResourceGroupHandlers:Function[] = [];
  private deleteResourceGroupHandlers:Function[] = [];

  private createResourceHandlers:Function[] = [];
  private modifyResourceHandlers:Function[] = [];
  private deleteResourceHandlers:Function[] = [];

  private modifyAccountHandlers:Function[] = [];

  private onCreateResourceGroup = (data)=>{
      this.createResourceGroupHandlers.forEach((cb)=>{ cb(data); });
  }

  private onModifyResourceGroup = (data)=>{
      this.modifyResourceGroupHandlers.forEach((cb)=>{ cb(data); });
  }

  private onDeleteResourceGroup = (data)=>{
      this.deleteResourceGroupHandlers.forEach((cb)=>{ cb(data); });
  }

  private onCreateResource = (data)=>{
    this.createResourceHandlers.forEach((cb)=>{ cb(data); });
  }

  private onModifyResource = (data)=>{
    this.modifyResourceHandlers.forEach((cb)=>{ cb(data); });
  }

  private onDeleteResource = (data)=>{
    this.deleteResourceHandlers.forEach((cb)=>{ cb(data); });
  }

  private onModifyAccount = (data)=>{
    this.modifyAccountHandlers.forEach((cb)=>{ cb(data); });
  }

  constructor(
    private serverRestApiService: ServerRestApiService, 
    private subscriptionService: SubscriptionService, 
    private authService: AuthService,
    private accountService: AccountService,
    private roomService: RoomService) {
    // is this service destroyable during the app, or permament? do we need to unsubscribe?
    this.subscriptionService.subscribe(SubscriptionServiceEvent.RESOURCE_GROUP_EVENT, SubscriptionServiceEventType.CREATE, this.onCreateResourceGroup);
    this.subscriptionService.subscribe(SubscriptionServiceEvent.RESOURCE_GROUP_EVENT, SubscriptionServiceEventType.MODIFY, this.onModifyResourceGroup);
    this.subscriptionService.subscribe(SubscriptionServiceEvent.RESOURCE_GROUP_EVENT, SubscriptionServiceEventType.DELETE, this.onDeleteResourceGroup);
  
    this.subscriptionService.subscribe(SubscriptionServiceEvent.ROOM_EVENT, SubscriptionServiceEventType.CREATE, this.onCreateResource);
    this.subscriptionService.subscribe(SubscriptionServiceEvent.ROOM_EVENT, SubscriptionServiceEventType.MODIFY, this.onModifyResource);
    this.subscriptionService.subscribe(SubscriptionServiceEvent.ROOM_EVENT, SubscriptionServiceEventType.DELETE, this.onDeleteResource);
  
    this.subscriptionService.subscribe(SubscriptionServiceEvent.ACCOUNT_EVENT, SubscriptionServiceEventType.MODIFY, this.onModifyAccount);
  }

  // get the workspace - this will be called after the subscription workspace query, so
  // this data is coming from the cache
  public getResourceTree(): Promise<WorkspaceQueryRecord>{
    return this.serverRestApiService.query({
      query: resourceTreeQuery, 
      variables: {
        prepareSub: false
      }
    }).then((res)=>{
        let ws = res;

        let resGroups = ws['resourceGroups'];

        
        let promises: Promise<void>[] = []; // need to get the user's name for the dialogues

        let allRes = [];
        
        if (resGroups){ // decrypt the group names
          resGroups.forEach(element => {
            // collect the all res in an arr
            if (element['resources'].length > 0){
              allRes = allRes.concat(element['resources'])
            }
          });
        }

        // decrypt the resource names
        if (ws['ungroupedResources'].length > 0){
          allRes = allRes.concat(ws['ungroupedResources'])
        }

        ws['grantedRooms'] = [];
        ws['grantedRoomIds'].forEach(roomId => {
          promises.push(this.roomService.getData(roomId).then((roomData)=>{
            ws['grantedRooms'].push(roomData);
          }));
        });
        
        allRes.forEach((element)=>{
          if (element['type'] == 'dialogue'){
            // the dialogue shows the partner user's id
            promises.push(this.accountService.getAccount(element['id']).then((user)=>{
              element['name'] = user['avatarName'];
              return;
            }));
          }
        })
        
        return Promise.all(promises).then(()=>{
          return res;
        })

      });
  }


  // mutations with the resource group -------------------------------------------
  public createResourceGroup(name: string){
    return this.serverRestApiService.mutate({
      query: createResourceGroupQuery,
      variables: {
        name
      },
      encrypt: this.encryptResourceGroupData.bind(this)
    });
  }

  public deleteResourceGroup(id){
    return this.serverRestApiService.mutate({
      query: deleteResourceGroupQuery,
      variables: {
        groupId: id
      }
    });
  }

  public editResourceGroupName(id, name: string): Promise<any>{
    return this.serverRestApiService.mutate({
      query: editResourceGroupNameQuery,
      variables: {
        groupId: id,
        name
      },
      encrypt: this.encryptResourceGroupData.bind(this)
    })
  }

  public editResourceGroupOrdering(id, ordering: number){
      return this.serverRestApiService.mutate({
        query: editResourceGroupOrderingQuery,
        variables: {
          groupId: id,
          ordering
        }
      });
  }

  public editResourceGroupClosed(id, closed: boolean){
    return this.serverRestApiService.mutate({
      query: editResourceGroupClosedQuery,
      variables: {
        groupId: id,
        closed
      }
    });
  }

  public encryptResourceGroupData(data): Promise<Uint8Array>{
    if (data['name']){
      return ACCOUNT_CRYPTO_CONTEXT.dump(
        new AccountConfigDumpArgs(new TextEncoder().encode(data['name']), this.authService.getSelfAccountKeyring())
      ).then((name)=>{
        data['name'] = name;
        return data;
      })
    }
    else{
      return Promise.resolve(data);
    }
  }






  // mutations with resources ---------------------------------------------------------------
  /**
   * Create a resource in the selected group. Or in the ungroup-group if the groupId is null
   * @param name name of the new resources
   * @param groupId destination groupId, ungroup if it is null
   * @returns 
   * 
   * THIS IS UNDER REFACTOR
   */
  public createResource(name: string, groupId = null){
    return null; /*this.serverRestApiService.mutate({
      query: createChatQuery,
      variables: {
        name
      }
    })*/
  }

  public deleteResource(id){
    // under refactor
    return Promise.resolve();
  }

  public editResourceName(id, name: string){
    return null; /*this.serverRestApiService.mutate({
      query: editChatNameQuery,
      variables: {
        id,
        name
      }
    })*/
  }
  
  public editResourceOrdering(id, groupId, ordering: number){
    
    return this.serverRestApiService.mutate({
      query: editRoomPlacementQuery,
      variables: {
        roomId: id,
        ordering,
        groupId
      } 
    })

  }

  public editDialogueOrdering(id, groupId, ordering: number){
    
    return this.serverRestApiService.mutate({
      query: editDialoguePlacementQuery,
      variables: {
        peerId: id,
        ordering,
        groupId
      } 
    })

  }

  

  

  
  // subscribe/unsubscribe any changes -----------------------------------------------------
  // resource group
  public subscribeOnCreateResourceGroup(cb){
    this.createResourceGroupHandlers.push(cb);
  }

  public subscribeOnDeleteResourceGroup(cb){
    this.deleteResourceGroupHandlers.push(cb);
  }

  public subscribeOnModifyResourceGroup(cb){
    this.modifyResourceGroupHandlers.push(cb);
  }

  public unsubscribeOnCreateResourceGroup(cb){
    this.createResourceGroupHandlers.splice(this.createResourceGroupHandlers.indexOf(cb), 1);
  }

  public unsubscribeOnDeleteResourceGroup(cb){
    this.deleteResourceGroupHandlers.splice(this.deleteResourceGroupHandlers.indexOf(cb), 1);
  }

  public unsubscribeOnModifyResourceGroup(cb){
    this.modifyResourceGroupHandlers.splice(this.modifyResourceGroupHandlers.indexOf(cb), 1);
  }

  
  // resources
  public subscribeOnCreateResource(cb){
    this.createResourceHandlers.push(cb);
  }

  public subscribeOnDeleteResource(cb){
    this.deleteResourceHandlers.push(cb);
  }

  public subscribeOnModifyResource(cb){
    this.modifyResourceHandlers.push(cb);
  }

  public unsubscribeOnCreateResource(cb){
    this.createResourceHandlers.splice(this.createResourceHandlers.indexOf(cb), 1);
  }

  public unsubscribeOnDeleteResource(cb){
    this.deleteResourceHandlers.splice(this.deleteResourceHandlers.indexOf(cb), 1);
  }

  public unsubscribeOnModifyResource(cb){
    this.modifyResourceHandlers.splice(this.modifyResourceHandlers.indexOf(cb), 1);
  }


  // account
  public subscribeOnModifyAccount(cb){
    this.modifyAccountHandlers.push(cb);
  }

  public unsubscribeOnModifyAccount(cb){
    this.modifyAccountHandlers.splice(this.modifyAccountHandlers.indexOf(cb), 1);
  }

  
  
}