import Promise from 'bluebird';
import EventEmitter, { IEmitter } from './Emitter';
import { IApiService } from './Api';
import { IAuthService } from './Auth';

export interface IOrganization extends IEmitter {
  getFolderUserAccess(): any;
  update(which: string, folder: any, data: any): any;
}

export default class Organization extends EventEmitter implements IOrganization {
  public folders: any[] = [];
  public filteredFolders: any[] = [];
  public disabledFolders: any[] = [];
  public users: any[] = [];
  public filteredUsers: any[] = [];
  public folderPages: any = [];
  public folderFilter: any = {
    name: ''
  };
  public userFilter: any = {
    emails: '',
    screen_name: '',
    full_name: ''
  };
  public accessLevels: any = {
    RO: 'Read-only',
    RW: 'Read/write',
    PA: 'Page Administrator',
    FA: 'Folder Administrator',
    NO: 'Suspend'
  };
  public folderUsers: any = {};
  public folderInvites: any = {};

  // folder loop vars
  private folderCount: number = 0;
  private folderQ: any;
  private folderPromiseResolve: any;
  private folderPromiseReject: any;

  constructor(private api: IApiService, private auth: IAuthService) {
    super();
  }

  public load(): any {
    // let q: any = this.$q.defer();
    let p: Promise<any> = new Promise((resolve, reject) => {
      this.folders = [];
      this.disabledFolders = [];
      this.api
        .getOrganizationDomains({
          organizationId: this.auth.user.organization_id
        })
        .then(results => {
          results.data.results.forEach((folder: any) => {
            // if (
            //     !folder.current_user_domain_access ||
            //     folder.current_user_domain_access.access_level !== "AD"
            // ) {
            //     return;
            // }
            let active: boolean = false;
            ['active_admin', 'active_opa', 'active_row', 'active_rw'].forEach(access => {
              if (folder.summary_domain_access[access] > 0) {
                active = true;
              }
            });
            folder._active = active;
            this.folders.push(folder);
          });
          // return (this.folders = results.data.results);
          this.queryFolders();
          resolve(true);
        });
    });
    return p;
  }

  public getUserFolderAcess(userId: number, folderId: number): any {
    let p: Promise<any> = new Promise((resolve, reject) => {
      let data: any = {
        user: undefined,
        pages: []
      };
      let error: any;
      this.api
        .domainUsers({ domainId: folderId })
        .then(result => {
          result.data.results.forEach((user: any) => {
            if (user.user.id !== userId) {
              return;
            }
            data.user = user;
            data.user.is_rw = ['RW', 'PA', 'FA'].indexOf(user.access_level) > -1 || user.is_administrator;
          });
          if (!data.user) {
            data.user = 'NO';
          }
          return this.api.getDomainPageAccess({ domainId: folderId });
        })
        .then(result => {
          let domains: any = result.data.user_domain_access;
          let pages: any = result.data.pages;
          let access: any = result.data.user_page_access;
          for (let p: number = 0; p < pages.length; p++) {
            let page: any = pages[p];
            page.access = {
              access: 'no',
              page_id: page.id,
              user_id: userId
            };
            for (let a: number = 0; a < access.length; a++) {
              let rights: any = access[a];
              if (rights.user_id === userId && rights.page_id === page.id) {
                page.access = rights;
                break;
              }
            }
            data.pages.push(page);
          }
        })
        .catch(err => {
          error = err;
        })
        .finally(() => {
          if (error) {
            reject(error);
            return;
          }
          resolve(data);
        });
    });
    return p;
  }

  public updateFolderStatus(f: any): any {
    let active: boolean = false;
    ['active_admin', 'active_opa', 'active_row', 'active_rw'].forEach(access => {
      if (f.summary_domain_access[access] > 0) {
        active = true;
      }
    });
    f._active = active;
    let inactive: boolean = false;
    ['inactive_admin', 'inactive_opa', 'inactive_row', 'inactive_rw'].forEach(access => {
      if (f.summary_domain_access[access] > 0) {
        inactive = true;
      }
    });
    f._inactive = inactive;
    return f;
  }

  public queryFolders(): void {
    let folders: any[] = [];
    this.folders.forEach(f => {
      f = this.updateFolderStatus(f);
      if (!this.folderFilter.name) {
        folders.push(f);
        return;
      }
      if (f.name.toLowerCase().indexOf(this.folderFilter.name.toLowerCase()) > -1) {
        folders.push(f);
      }
    });
    folders.sort((a, b) => {
      if (a.name.toLowerCase() > b.name.toLowerCase()) {
        return 1;
      }
      if (a.name.toLowerCase() < b.name.toLowerCase()) {
        return -1;
      }
      return 0;
    });
    this.filteredFolders = folders;
  }

  public queryUsers(): void {
    let users: any[] = [];
    this.users.forEach(f => {
      // if (!this.filter) {
      //     users.push(f);
      //     return;
      // }
      let add: boolean = true;
      if (this.userFilter.emails) {
        let emails: any[] = this.userFilter.emails.split(',');
        add = false;
        emails.forEach(email => {
          if (!email.trim()) {
            return;
          }
          if (f.email.indexOf(email.trim()) > -1) {
            add = true;
          }
        });
      }
      if (this.userFilter.screen_name && f.screen_name.indexOf(this.userFilter.screen_name) === -1) {
        add = false;
      }
      if (this.userFilter.full_name && f.full_name.indexOf(this.userFilter.full_name) === -1) {
        add = false;
      }
      if (add) {
        users.push(f);
      }
    });
    users.sort((a, b) => {
      if (a.email > b.email) {
        return 1;
      }
      if (a.email < b.email) {
        return -1;
      }
      return 0;
    });
    this.filteredUsers = users;
  }

  public inviteUsers(folder: any, users: any): any {
    let p: Promise<any> = new Promise((resolve, reject) => {
      let emails: any = [];
      users.forEach((user: any) => {
        emails.push({ email: user.email });
      });
      let requestData: any = {
        domainId: folder.id,
        data: {
          emails: emails,
          message: ''
        }
      };
      this.api
        .inviteUsers(requestData)
        .then(response => {
          console.log(response);

          response.data.forEach((invitation: any) => {
            if (!this.folderInvites[folder.id]) {
              this.folderInvites[folder.id] = {};
            }
            if (!this.folderUsers[folder.id]) {
              this.folderUsers[folder.id] = {};
            }
            this.folderInvites[folder.id][invitation.invited_user.id] = invitation;
            this.folderUsers[folder.id][invitation.invited_user.id] = {
              access_level: 'RW',
              is_pending: invitation.domain_access.is_pending,
              is_active: !invitation.domain_access.is_pending,
              user: invitation.invited_user
            };
          });

          // this.toastr.success("User has been invited to folder");
          resolve(response);
        })
        .catch(err => {
          // this.toastr.error(err.message);
          reject(err);
        });
    });
    return p;
  }

  public removeInvites(folder: any, invitations: any): any {
    let p: Promise<any> = new Promise((resolve, reject) => {
      let invites: any = [];
      invitations.forEach((invite: any) => {
        invites.push({ id: invite.id });
      });
      let requestData: any = {
        domainId: folder.id,
        data: invites
      };

      // for (let i: number = 0; i < invitations.length; i++) {
      //     requestData.data.push({ id: invitations[i].id });
      // }
      this.api.cancelInvitations(requestData).then(
        result => {
          // this.toastr.success("Invitation canceled");
          invitations.forEach((invite: any) => {
            this.folderUsers[folder.id][invite.invited_user.id] = false;
            this.folderInvites[folder.id][invite.invited_user.id] = false;
          });
          resolve(result);
        },
        err => {
          // this.toastr.error("Could not cancel invitaion");
          reject(err);
        }
      );
    });
    return p;
  }

  public setUserAccess(folder: any, users: any, level: any, active: boolean = false): any {
    let p: Promise<any> = new Promise((resolve, reject) => {
      let items: any = [];

      let isActive: boolean = level === 'NO' || active === false ? false : true;
      level = level === 'NO' ? undefined : level;

      users.forEach((user: any) => {
        if (user.level) {
          level = user.level;
        }
        items.push({
          user_id: user.id,
          access_level: level === 'NO' ? undefined : level,
          is_active: isActive
        });
      });

      let requestData: any = {
        domainId: folder.id,
        data: items
      };
      this.api
        .updateDomainAccess(requestData)
        .then(response => {
          // this.toastr.success("User access updated");
          users.forEach((user: any) => {
            if (!this.folderUsers[folder.id]) {
              this.folderUsers[folder.id] = {};
            }
            if (level) {
              this.folderUsers[folder.id][user.id].access_level = level;
            }
            this.folderUsers[folder.id][user.id].is_active = isActive;
          });
          resolve(response);
        })
        .catch(err => {
          // this.toastr.error(err.message);
          reject(err);
        });
    });
    return p;
  }

  public removeUserAccess(folder: any, users: any): any {
    let p: Promise<any> = new Promise((resolve, reject) => {
      let items: any = [];
      users.forEach((user: any) => {
        items.push({
          user_id: user.id
        });
      });
      let requestData: any = {
        domainId: folder.id,
        data: items
      };

      this.api.removeUsersFromDomain(requestData).then(
        result => {
          // this.toastr.success("User removed");
          users.forEach((user: any) => {
            this.folderUsers[folder.id][user.id] = false;
          });
          resolve(result);
        },
        err => {
          // this.toastr.error("Could not remove user from folder");
          reject(err);
        }
      );
    });
    return p;
  }

  public updateUserPageAccess(folderId: number, pages: any, access: string, selected: boolean = false): any {
    let p: Promise<any> = new Promise((resolve, reject) => {
      let items: any = [];
      access = access === 'no' ? '' : access;
      pages.forEach((page: any) => {
        if (selected && !page._selected) {
          return;
        }
        items.push({
          user_id: page.access.user_id,
          page_id: page.access.page_id,
          access: access
        });
      });
      let data: any = {
        domainId: folderId,
        data: items
      };
      if (!items.length) {
        reject({
          message: 'No pages selected'
        });
        return;
      }
      this.api
        .saveDomainPageAccess(data)
        .then(response => {
          // this.toastr.success("Access rights updated");
          pages.forEach((page: any) => {
            if (selected && !page._selected) {
              return;
            }
            page.access.access = access || 'no';
          });
          resolve(true);
        })
        .catch(err => {
          reject(err);
        });
    });
    return p;
  }

  public getTooltipStatus(user: any): string {
    if (user.is_pending) {
      return 'Pending User';
    }
    if (user.is_active) {
      return 'Active User';
    }
    return 'Suspended User';
  }

  public getStatusClass(user: any): string {
    if (user.is_pending) {
      return 'pending';
    }
    if (user.is_active) {
      return 'active';
    }
    return 'inactive';
  }

  public update(which: string, folder: any, data: any): any {
    if (which === 'invite') {
      let users: any = [];
      this.filteredUsers.forEach(user => {
        if (Object.keys(this.folderUsers[folder.id][user.id]).length) {
          return;
        }
        users.push(user);
      });
      if (users.length) {
        return this.inviteUsers(folder, users);
      }
    }
    if (which === 'cancel') {
      let invitations: any = [];
      this.filteredUsers.forEach(user => {
        if (!this.folderInvites[folder.id] || !this.folderInvites[folder.id][user.id]) {
          return;
        }
        invitations.push(this.folderInvites[folder.id][user.id]);
      });
      if (invitations.length) {
        return this.removeInvites(folder, invitations);
      }
    }
    if (which === 'access') {
      let users: any = [];
      this.filteredUsers.forEach(user => {
        if (
          !this.folderUsers[folder.id] ||
          !Object.keys(this.folderUsers[folder.id][user.id]).length ||
          this.folderUsers[folder.id][user.id].is_pending
          // user.id === this.app.user.id
        ) {
          return;
        }
        users.push(user);
      });
      if (users.length) {
        return this.setUserAccess(folder, users, data);
      }
    }
    if (which === 'remove') {
      let users: any = [];
      for (let u in this.folderUsers[folder.id]) {
        if (!Object.keys(this.folderUsers[folder.id][u]).length || this.folderUsers[folder.id][u].is_active) {
          continue;
        }
        users.push({
          id: this.folderUsers[folder.id][u].user.id
        });
      }
      if (users.length) {
        return this.removeUserAccess(folder, users);
      }
    }
    if (which === 'activate') {
      let users: any = [];
      for (let u in this.folderUsers[folder.id]) {
        if (
          !Object.keys(this.folderUsers[folder.id][u]).length ||
          this.folderUsers[folder.id][u].is_active ||
          this.folderUsers[folder.id][u].is_pending
        ) {
          continue;
        }
        users.push({
          id: this.folderUsers[folder.id][u].user.id,
          level: this.folderUsers[folder.id][u].access_level
        });
      }
      if (users.length) {
        return this.setUserAccess(folder, users, '', true);
      }
    }
    let p: Promise<any> = new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve();
      });
    });
    return p;
    // let q: any = this.$q.defer();
    // this.$timeout(() => {
    //   resolve();
    // }, 10);
    // return p;
  }

  public getFolderUserAccess(): any {
    let p: Promise<any> = new Promise((resolve, reject) => {
      // get folders
      this.api
        .getOrganizationDomains({
          organizationId: this.auth.user.organization_id
        })
        .then(results => {
          this.folders = results.data.results;
          // get users
          return this.api.getOrganizationUsers({
            organizationId: this.auth.user.organization_id
          });
        })
        .then(results => {
          this.users = results.data.results;
          // get access for each domain. eeek
          return this.getDomainUsersQ();
        })
        .then(results => {
          return true;
        })
        .catch(err => {
          console.log(err);
        })
        .finally(() => {
          this.queryFolders();
          this.queryUsers();
          resolve({ users: this.filteredUsers, folders: this.filteredFolders });
        });
    });
    return p;
  }

  private getDomainUsersQ(): any {
    this.folderCount = 0;
    let p: Promise<any> = new Promise((resolve, reject) => {
      this.folderPromiseResolve = resolve;
      this.folderPromiseReject = reject;
    });
    // this.folderQ = this.$q.defer();
    this.getDomainUsers();
    return p;
  }

  private getDomainUsers(): any {
    let folder: any = this.folders[this.folderCount];
    if (!this.folderUsers[folder.id]) {
      this.folderUsers[folder.id] = {};
    }
    if (!this.folderInvites[folder.id]) {
      this.folderInvites[folder.id] = {};
    }
    this.api
      .domainUsers({ domainId: folder.id })
      .then(results => {
        results.data.results.forEach((permission: any) => {
          // TODO: Stats
          permission._stats = {
            pushes: Math.floor(Math.random() * 1500)
          };
          this.folderUsers[folder.id][permission.user.id] = permission;
        });
        // get folder invitations
        return this.api.domainInvitations({ domainId: folder.id });
      })
      .then(results => {
        results.data.results.forEach((invitation: any) => {
          this.folderInvites[folder.id][invitation.invited_user.id] = invitation;
        });
        return true;
      })
      .catch(() => {
        this.folderUsers[folder.id] = false;
        this.folderInvites[folder.id] = false;
      })
      .finally(() => {
        // assign missing users
        this.users.forEach((user: any) => {
          if (!this.folderUsers[folder.id][user.id]) {
            this.folderUsers[folder.id][user.id] = {};
          }
        });

        this.folderCount++;
        if (this.folderCount >= this.folders.length) {
          // stop!
          this.folderPromiseResolve(true);
          return;
        }
        this.getDomainUsers();
      });
  }
}
