import Emitter, { IEmitter } from './Emitter';
import { IApiService, IRequestResult, ApiError } from './Api';
import { IAuthService, IUserSelf } from './Auth';
import { types } from 'util';
import Promise from 'bluebird';

let api: IApiService, auth: IAuthService;

interface IFolderWrap {
  Folder: Folder;
}

export class FolderWrap {
  constructor(
    // q: IQService,
    // timeout: ITimeoutService,
    // interval: IIntervalService,
    ippApi: IApiService,
    ippAuth: IAuthService
  ) {
    api = ippApi;
    auth = ippAuth;
  }
}

export enum IFolderDataUserAccessLevel {
  FA = 'FA',
  PA = 'PA',
  RW = 'RW',
  RO = 'RO'
}
export interface IFolderDataUserAccess {
  default_page_id: number;
  default_page_url: string;
  domain_id: number;
  domain_url: string;
  is_active: boolean;
  is_administrator: boolean;
  access_level: IFolderDataUserAccessLevel;
  is_pending: false;
  is_default_domain: false;
  page_count: number;
  user_id: number;
  user_url: string;
}
export interface IFolderDataModifiedBy {
  id: number;
  url: string;
  screen_name: string;
  first_name: string;
  last_name: string;
}
export interface IFolderDataCreatedBy {
  id: number;
  url: string;
  screen_name: string;
  first_name: string;
  last_name: string;
}
export interface IFolderData {
  id: number;
  url: string;
  by_name_url: string;
  name: string;
  display_name: string;
  description: string;
  encryption_enabled: boolean;
  logo_url: string;
  login_screen_background_color: string;
  page_access_url: string;
  access_groups_url: string;
  users_url: string;
  created_by: IFolderDataCreatedBy;
  created_timestamp: string;
  modified_by: IFolderDataModifiedBy;
  modified_timestamp: string;
  current_user_domain_access: IFolderDataUserAccess;
  page_access_mode: number;
  is_page_access_mode_selectable: boolean;
  domain_type: boolean;
  is_paying_customer: boolean;
  default_push_interval: number;
  default_pull_interval: number;
  email_updates: boolean;
  email_updates_new_page: boolean;
  default_ws_enabled: boolean;
  default_symphony_sid: string;
}

export interface IFolderHistory {
  created_timestamp: string;
  agent: string;
  action_type: string;
  action_type_display: string;
  object: any;
}

export interface IFolder extends IEmitter {
  PAGE_ACCESS_BASIC: number;
  PAGE_ACCESS_ENTERPRISE: number;
  hasAccess: boolean;
  id: number | string;
  data: IFolderData;
  history: IFolderHistory[];
  load(id: number): Promise<any>;
  getData(): Promise<any>;
  getHistory(limit?: number): Promise<any>;
  getUserAccess(pageId?: number): Promise<any>;
  getOrgUsers(): Promise<any>;
  getDomainInvitations(): Promise<any>;
  getDomainUsers(): Promise<any>;
  updateUserDomainAccess(users: IFolderUser[], level?: IFolderDataUserAccessLevel, active?: boolean): Promise<any>;
  removeUserDomainAccess(users: IFolderUser[]): Promise<any>;
  addUserDomainAccess(users: IFolderOrgUser[], isEnterprise: boolean): Promise<any>;
  cancelUserDomainAccess(users: IFolderUser[]): Promise<any>;
}

export interface IFolderOrgUser {
  id: number;
  email: string;
  text: string;
  first_name: string;
  is_active: boolean;
  is_organization_admin: boolean;
  job_title: string;
  last_login: string;
  last_name: string;
}

export interface IFolderUserAccess {
  user_id: number;
  page_id: number;
  access: '';
}

export interface IFolderUserUser {
  id: number;
  email: string;
  email_count?: number;
  invited_user?: any;
}

export interface IFolderUser {
  access_level: IFolderDataUserAccessLevel;
  default_page_id: number | null;
  default_page_url: number | null;
  domain_id: number;
  domain_url: string;
  is_active: boolean;
  is_administrator: boolean;
  is_default_domain: boolean;
  is_pending: boolean;
  user: IFolderUserUser;
  own_page: boolean;
  access: IFolderUserAccess;
  invitation?: IFolderInvitation;
  _administrator?: boolean;
}

export interface IFolderPage {
  id: number;
  is_public: boolean;
  name: string;
  own_page: boolean;
  special_page_type: number;
}

export interface IFolderInvitationUser {
  id: number;
  url: string;
  screen_name: string;
  first_name: string;
  last_name: string;
}
export interface IFolderInvitationDomainAccess {
  is_active: boolean;
  is_administrator: boolean;
  is_pending: boolean;
}
export interface IFolderInvitationCreatedBy {
  id: number;
  url: string;
  screen_name: string;
  first_name: string;
  last_name: string;
}
export interface IFolderInvitation {
  id: number;
  url: string;
  email: string;
  invited_user: IFolderInvitationUser;
  domain: number;
  domain_access: IFolderInvitationDomainAccess;
  type: string;
  type_display: string;
  status: string; // eg pending
  status_display: string;
  email_count: number;
  created_timestamp: string;
  created_by: IFolderInvitationCreatedBy;
  completed_timestamp: string | null;
  completed_by: string | null;
  is_complete: boolean;
  customer: any;
}

export class Folder extends Emitter implements IFolder {
  public id: number = 0;
  public hasAccess: boolean = false;
  public data!: IFolderData;
  public history: IFolderHistory[] = [];
  public orgUsers: IFolderOrgUser[] = [];
  public users: IFolderUser[] = [];
  public currentUsers: IFolderUser[] = [];
  public suspendedUsers: IFolderUser[] = [];
  public pendingUsers: IFolderUser[] = [];
  public invitedUsers: IFolderInvitation[] = [];
  public pendingAccess: any[] = [];
  public get PAGE_ACCESS_BASIC(): number {
    return 1;
  }
  public get PAGE_ACCESS_ENTERPRISE(): number {
    return 0;
  }
  constructor(id?: number) {
    super();
    if (id) this.id = id;
  }
  public load(id: number, pageId?: number): Promise<any> {
    this.id = parseInt(`${id}`);
    this.hasAccess = false;
    let p: Promise<any> = new Promise((resolve, reject) => {
      this.getData()
        .then(() => {
          return this.getHistory();
        })
        .then(() => {
          return this.getUserAccess(pageId);
        })
        .then(() => {
          return this.getOrgUsers();
        })
        .then(() => {
          return this.getDomainInvitations();
        })
        .then(() => {
          return this.getDomainUsers();
        })
        .then(() => {
          resolve({
            data: this.data,
            history: this.history,
            hasAccess: this.hasAccess,
            orgUsers: this.orgUsers,
            currentUsers: this.currentUsers,
            suspendedUsers: this.suspendedUsers,
            pendingUsers: this.pendingUsers,
            pendingAccess: this.pendingAccess,
            invitedUsers: this.invitedUsers,
            users: this.users
          });
        })
        .catch(reject);
    });
    return p;
  }
  public getPageAccess(): Promise<any> {
    let p: Promise<any> = new Promise((resolve, reject) => {
      this.getData()
        .then(() => {
          api.getDomainPageAccess({ domainId: this.id }).then(result => {
            let userPageAccess = result.data.user_page_access;
            let users = result.data.user_domain_access;
            let domainPages = result.data.pages;
            let access: any = {};
            userPageAccess.forEach((level: any) => {
              if (!access[level.page_id]) {
                access[level.page_id] = {};
              }
              access[level.page_id][level.user_id] = level.access;
            });
    
            let sheets: any = [],
              pdfs: any = [],
              liveusages: any = [],
              singledomains: any = [],
              domainpages: any = [],
              alerts: any = [],
              others: any = [];
    
            domainPages.forEach((page: any) => {
              if (!access[page.id]) {
                access[page.id] = {};
              }
              users.forEach((user: IFolderUser) => {
                if (!access[page.id][user.user.id]) {
                  access[page.id][user.user.id] = 'no';
                }
              });
              if (page.special_page_type === 0) {
                sheets.push(page);
              } else if (page.special_page_type === 6) {
                pdfs.push(page);
              } else if (page.special_page_type === 5) {
                alerts.push(page);
              } else if (page.special_page_type === 4) {
                domainpages.push(page);
              } else if (page.special_page_type === 2) {
                singledomains.push(page);
              } else if (page.special_page_type === 7) {
                liveusages.push(page);
              } else {
                others.push(page);
              }
            });
            const sort = (a: any, b: any) => {
              if (a.user) {
                if (a.user.email > b.user.email) return 1;
                if (a.user.email < b.user.email) return -1;
                return 0;
              }
              if (a.name > b.name) return 1;
              if (a.name < b.name) return -1;
              return 0;
            };
            const mapUser = (user: IFolderUser) => {
              user._administrator = ['FA'].indexOf(user.access_level) > -1 || user.is_administrator;
              return user;
            };
            let pages: any = [].concat(
              pdfs.sort(sort),
              sheets.sort(sort),
              singledomains.sort(sort),
              domainpages.sort(sort),
              liveusages.sort(sort),
              alerts.sort(sort),
              others.sort(sort)
            );
    
            resolve({
              data: this.data,
              users: users.map(mapUser).sort(sort),
              pages,
              access
            });
          }, reject);
        })
        .catch(reject);
    });
    return p;
  }
  public getData(): Promise<any> {
    let p: Promise<any> = new Promise((resolve, reject) => {
      api
        .getDomain(this.id)
        .then((res: any) => {
          this.data = res.data;
          this.hasAccess =
            this.data.current_user_domain_access &&
            (this.data.current_user_domain_access.access_level === IFolderDataUserAccessLevel.PA ||
              this.data.current_user_domain_access.access_level === IFolderDataUserAccessLevel.FA ||
              this.data.current_user_domain_access.is_administrator);
          resolve(this.data);
        })
        .catch(reject);
    });
    return p;
  }
  public getHistory(limit: number = 15): Promise<any> {
    let p: Promise<any> = new Promise((resolve, reject) => {
      api
        .domainAccessLog({ domainId: this.id, limit })
        .then(res => {
          this.history = res.data.results;
          resolve(this.history);
        })
        .catch(resolve);
    });
    return p;
  }
  public getUserAccess(pageId?: number): Promise<any> {
    let p: Promise<any> = new Promise((resolve, reject) => {
      api.getDomainPageAccess({ domainId: this.id }).then(result => {
        let users = result.data.user_domain_access;
        let pages = result.data.user_page_access;
        let ownPage;

        users.forEach((user: IFolderUser) => {
          // user.text = user.user.email;
          user.own_page = false;
          user.access = {
            user_id: user.user.id,
            page_id: pageId || 0,
            access: ''
          };
          if (user.user.id === auth.user.id) {
            result.data.pages.forEach((page: any) => {
              if (page.id !== pageId) return;
              user.own_page = page.own_page;
            });
          }
          pages.forEach((page: any) => {
            if (page.page_id !== pageId || page.user_id !== user.user.id) {
              return;
            }
            user.access = page;
          });
        });
        this.users = users;
        resolve(users);
      }).catch(reject);
    });
    return p;
  }
  public getOrgUsers(): Promise<any> {
    let p: Promise<any> = new Promise((resolve, reject) => {
      if (!auth.user.organization) {
        resolve();
        return;
      }
      api
        .getOrganizationLinkedUsers({ organizationId: auth.user.organization_id })
        .then(res => {
          const users: IFolderOrgUser[] = [];
          const emails: string[] = [];
          this.users.forEach(user => {
            emails.push(user.user.email);
          });
          res.data.results.forEach((user: IFolderOrgUser) => {
            // if (emails.indexOf(user.email) > -1) {
            //   return;
            // }
            user.text = `${user.first_name} ${user.last_name} (${user.email})`;
            users.push(user);
          });
          this.orgUsers = users;
          resolve(this.orgUsers);
        })
        .catch(resolve);
    });
    return p;
  }
  public getDomainInvitations(): Promise<any> {
    let p: Promise<any> = new Promise((resolve, reject) => {
      // Get domain invitations
      api.domainInvitations({ domainId: this.id }).then(result => {
        this.invitedUsers = result.data.results;
        resolve(this.invitedUsers);
      }, reject); // @todo: deal with error
    });
    return p;
  }
  public getDomainUsers(): Promise<any> {
    let p: Promise<any> = new Promise((resolve, reject) => {
      api.domainUsers({ domainId: this.id }).then(result => {
        this.currentUsers = [];
        this.suspendedUsers = [];
        result.data.results.forEach((user: IFolderUser) => {
          if (!this.pendingAccess[user.user.id] || !this.pendingAccess[user.user.id]._dirty) {
            this.pendingAccess[user.user.id] = user;
          }
          if (user.is_active) {
            this.currentUsers.push(user);
          } else if (!user.is_pending) {
            this.suspendedUsers.push(user);
          } else if (user.is_pending) {
            // find invitation
            this.invitedUsers.forEach(invitation => {
              if (invitation.email !== user.user.email) return;
              user.invitation = invitation;
            });
            this.pendingUsers.push(user);
          }
        });
        resolve({
          currentUsers: this.currentUsers,
          suspendedUsers: this.suspendedUsers,
        });
      }, reject); // @todo: deal with error
    });
    return p;
  }
  public updateUserDomainAccess(
    users: IFolderUser[],
    level?: IFolderDataUserAccessLevel,
    active?: boolean
  ): Promise<any> {
    let p: Promise<any> = new Promise((resolve, reject) => {
      let requestData: any = {
        domainId: this.id,
        data: []
      };
      users.forEach(user => {
        requestData.data.push({
          user_id: user.user.invited_user ? user.user.invited_user.id : user.user.id,
          access_level: typeof level !== 'string' ? user.access_level : level,
          is_active: typeof active !== 'boolean' ? user.is_active : active
        });
      });
      api.updateDomainAccess(requestData).then(() => {
        users.forEach(user => {
          if (level) user.access_level = level;
          if (typeof active === 'boolean') user.is_active = active;
        });
        resolve(users);
      }, reject);
    });
    return p;
  }
  public removeUserDomainAccess(users: IFolderUser[]): Promise<any> {
    let p: Promise<any> = new Promise((resolve, reject) => {
      const ids: any[] = [];
      users.forEach((user: IFolderUser) => {
        // if (!user.invitation) return;
        ids.push({ user_id: user.user.id });
      });
      let requestData = {
        domainId: this.id,
        data: ids
      };
      api.removeUsersFromDomain(requestData).then(() => {
        resolve(users);
      }, reject);
    });
    return p;
  }
  public addUserDomainAccess(users: IFolderOrgUser[], isEnterprise: boolean): Promise<any> {
    let p: Promise<any> = new Promise((resolve, reject) => {
      const emails: any = [];
      users.forEach((friend: IFolderOrgUser) => {
        emails.push({ email: friend.email });
      });
      let requestData = {
        domainId: this.id,
        data: {
          emails: emails
        }
      };
      api.inviteUsers(requestData).then(results => {
        resolve(results.data);
      }, reject);
    });
    return p;
  }
  public cancelUserDomainAccess(users: IFolderUser[]): Promise<any> {
    let p: Promise<any> = new Promise((resolve, reject) => {
      const ids: any[] = [];
      users.forEach((user: IFolderUser) => {
        if (user.invitation) {
          ids.push({ id: user.invitation.id });
        } else if (user.user && user.user.invited_user) {
          ids.push({ id: user.user.id});
        }
      });
      let requestData = {
        domainId: this.id,
        data: ids
      };
      api.cancelInvitations(requestData).then(results => {
        resolve(results.data);
      }, reject);
    });
    return p;
  }
}
