import Promise from 'bluebird';
import { IApiService } from './Api';
import { IStorageService } from './Storage';
import Helpers, { IHelpers } from './Helpers';

const helpers: IHelpers = new Helpers();

export interface IUserDataService {
  META_HIDE_TUTORIAL: string;
  META_FAVS: string;
  META_LOGO: string;
  META_RECENTS: string;
  META_WORKSPACES: string;
  META_ACTIVE_WORKSPACE: string;
  userId: number;
  get(key?: string, defaultValue?: any, parse?: boolean): any;
  set(key: string, value: any, stringify?: boolean): Promise<any>;
  parse: (data: any) => void;
  refresh: () => any;
  destroy(): void;
  //   getShortcuts: () => any;
  //   setShortcuts(data: any): any;
  getFavs(): Promise<any>;
  updateFav(data: IUserDataFav): Promise<any>
  addFav(data: IUserDataFav): Promise<any>;
  removeFav(data: IUserDataFav): Promise<any>;
  toggleFav(data: IUserDataFav): Promise<any>;
  getFavLink(folderId: number, pageId: number): string;
  isFav(folderId: number, pageId: number): boolean;

  getRecents(): IUserDataFav[];
  updateRecent(data: IUserDataFav): void;
  addRecent(data: IUserDataFav): void;

  saveWorkspaces(workspaces: IUserDataWorkspace[]): Promise<any>;
  getWorkspaces(): Promise<any>;
  getNewWorkspace(): IUserDataWorkspace;
  addWorkspace(data: IUserDataWorkspace): Promise<any>;
  removeWorkspace(id: string): Promise<any>;
}

export interface IUserDataFav {
  link: string;
  title: string;
  type: number;
}
export interface IUserDataWorkspaceTileCoords {
  x: number;
  y: number;
  w: number;
  h: number;
  z: number;
}
export interface IUserDataWorkspaceTile {
  title: string;
  type: string;
  domain: string;
  page: string;
  tile: IUserDataWorkspaceTileCoords;
  fit?: string;
  state?: string;
}
export interface IUserDataWorkspace {
  name: string;
  id: string;
  freeze: false;
  boards: IUserDataWorkspaceTile[];
  unit: '%'
}

class UserData implements IUserDataService {
  public get META_HIDE_TUTORIAL(): string {
    return 'hide_tutorial';
  }
  public get META_FAVS(): string {
    return 'shortcuts';
  }
  public get META_LOGO(): string {
    return 'logo';
  }
  public get META_RECENTS(): string {
    return 'recents';
  }
  public get META_WORKSPACES(): string {
    return 'workspaces';
  }
  public get META_ACTIVE_WORKSPACE(): string {
    return 'active_workspace';
  }
  public recentsLimit: number = 10;

  private _data: any = {};
  private _favs: IUserDataFav[] = [];
  private _links: IUserDataFav[] = [];
  private _workspaces: IUserDataWorkspace[] = [];

  constructor(public api: IApiService, public storage: IStorageService, public userId: number) {
    this._links = storage.user.get(this.META_RECENTS, []);
    return;
  }

  public get favs() {
    return this._favs;
  }

  // convert key value from api data into object
  public parse(data: any): void {
    this._data = {};
    if (data === undefined) {
      return;
    }
    let i: number;
    for (i = 0; i < data.length; i++) {
      this._data[data[i].key] = data[i].value;
    }
    this._favs = this.get(this.META_FAVS, [], true) || [];
    // this._links = this.storage.user.get(this.META_RECENTS, []);
    this._workspaces = this.get(this.META_WORKSPACES, [], true) || [];
    return this._data;
  }

  public set(key: string, value: any, stringify?: boolean): Promise<any> {
    this._data[key] = value;
    return this.api.saveUserMetaData({
      userId: this.userId,
      data: [{ key: key, value: stringify ? JSON.stringify(value) : value }]
    });
  }

  public refresh(): any {
    return this.api
      .getUserMetaData({
        userId: this.userId
      })
      .then(response => {
        return this.parse(response.data);
      });
  }

  public get(key?: string, defaultValue?: any, parse?: boolean): any {
    if (key) {
      if (this._data.hasOwnProperty(key)) {
        try {
          return parse ? JSON.parse(this._data[key]) : this._data[key];
        } catch (e) {
          return this._data[key];
        }
      }
      return defaultValue || null;
    }
    return this._data;
  }

  public destroy(): void {
    this._data = {};
  }

  public getRecents(): IUserDataFav[] {
    this._links = this.storage.user.get(this.META_RECENTS, []);
    return this._links;
  }
  public updateRecent(data: IUserDataFav): void {
    this._links.forEach((ln, index) => {
      if (ln.link != data.link) return;
      this._links[index] = data;
    });
    this.saveRecents();
  }
  public addRecent(data: IUserDataFav): void {
    this._links.forEach((ln, index) => {
      if (ln.link == data.link) {
        this._links.splice(index, 1);
      }
    });
    this._links.splice(0, 0, data);
    if (this._links.length > this.recentsLimit) {
      this._links.pop();
    }
    this.saveRecents();
  }

  public getFavs(): Promise<any> {
    let p: Promise<any> = new Promise((resolve, reject) => {
      this.refresh()
        .then(() => {
          resolve(this._favs);
        })
        .catch(reject);
    });
    return p;
  }
  public updateFav(data: IUserDataFav): Promise<any> {
    let p: Promise<any> = new Promise((resolve, reject) => {
      this.refresh()
        .then(() => {
          let index: number = -1;

          for (let i: number = 0; i < this._favs.length; i++) {
            if (this._favs[i].link !== data.link) continue;
            index = i;
            this._favs[i] = data;
          }

          if (index < 0) {
            resolve({ message: 'Favourite does not exist', favs: this._favs });
            return;
          }

          this.set(this.META_FAVS, this._favs, true)
            .then(() => {
              resolve({ message: 'Favourite has been updated', favs: this._favs });
            })
            .catch(reject);
        })
        .catch(reject);
    });
    return p;  
  }
  public setFavName(folderId: number, pageId: number, title: string): boolean {
    let link = this.getFavLink(folderId, pageId);
    let index: number = -1;

    for (let i: number = 0; i < this._favs.length; i++) {
      if (this._favs[i].link !== link) continue;
      index = i;
    }

    if (index < 0 || this._favs[index].title == title) {
      return false;
    }    
    this._favs[index].title = title;
    return true;
  }
  public saveFavs(): Promise<any> {
    let p: Promise<any> = new Promise((resolve, reject) => {
      this.set(this.META_FAVS, this._favs, true)
      .then(() => {
        resolve({ message: 'Favourites has been updated', favs: this._favs });
      })
      .catch(reject);
    });
    return p;
  }  
  public addFav(data: IUserDataFav): Promise<any> {
    let p: Promise<any> = new Promise((resolve, reject) => {
      this.refresh()
        .then(() => {
          if (!this.checkForFavDuplicate(data.link)) {
            this._favs.push(data);
            this.set(this.META_FAVS, this._favs, true)
              .then(() => {
                resolve({ message: 'Favourite has been saved', favs: this._favs });
              })
              .catch(reject);
          } else {
            resolve({ message: 'Favourite already exists', favs: this._favs });
          }
        })
        .catch(reject);
    });
    return p;
  }
  public removeFav(data: IUserDataFav): Promise<any> {
    let p: Promise<any> = new Promise((resolve, reject) => {
      this.refresh()
        .then(() => {
          let index: number = -1;

          for (let i: number = 0; i < this._favs.length; i++) {
            if (this._favs[i].link === data.link) {
              this._favs.splice(i, 1);
              index = i;
              break;
            }
          }

          if (index < 0) {
            resolve({ message: 'Favourite already removed', favs: this._favs });
            return;
          }

          this.set(this.META_FAVS, this._favs, true)
            .then(() => {
              resolve({ message: 'Favourite has been removed', favs: this._favs });
            })
            .catch(reject);
        })
        .catch(reject);
    });
    return p;
  }
  public toggleFav(data: IUserDataFav): Promise<any> {
    if (this.checkForFavDuplicate(data.link)) {
      return this.removeFav(data);
    } else {
      return this.addFav(data);
    }
  }
  public getFavLink(folderId: number, pageId: number): string {
    return `/domains/${folderId}/pages/${pageId}`;
  }
  public isFav(folderId: number, pageId: number): boolean {
    return this.checkForFavDuplicate(this.getFavLink(folderId, pageId));
  }

  public saveWorkspaces(workspaces: IUserDataWorkspace[]): Promise<any> {
    let p: Promise<any> = new Promise((resolve, reject) => {
      this.refresh()
        .then(() => {
          this.set(this.META_WORKSPACES, workspaces, true)
            .then(() => {
              resolve({ message: 'Workspaces saved', workspaces: this._workspaces });
            })
            .catch(reject);
        })
        .catch(reject);
    });
    return p;
  }

  public getNewWorkspace(): IUserDataWorkspace {
    return {
      name: 'Workspace',
      boards: [],
      freeze: false,
      id: helpers.getUuid(),
      unit: '%'
    };
  }

  public getWorkspaces(): Promise<any> {
    let p: Promise<any> = new Promise((resolve, reject) => {
      this.refresh()
        .then(() => {
          resolve(this._workspaces);
        })
        .catch(reject);
    });
    return p;
  }

  public addWorkspace(data: IUserDataWorkspace): Promise<any> {
    let p: Promise<any> = new Promise((resolve, reject) => {
      this.refresh()
        .then(() => {
          this._workspaces.push(data);
          this.set(this.META_WORKSPACES, this._workspaces, true)
            .then(() => {
              resolve({ message: 'Workspace has been added', workspaces: this._workspaces });
            })
            .catch(reject);
        })
        .catch(reject);
    });
    return p;
  }

  public removeWorkspace(id: string): Promise<any> {
    let p: Promise<any> = new Promise((resolve, reject) => {
      this.refresh()
        .then(() => {
          let index: number = -1;

          for (let i: number = 0; i < this._workspaces.length; i++) {
            if (this._workspaces[i].id === id) {
              this._workspaces.splice(i, 1);
              index = i;
              break;
            }
          }

          if (index < 0) {
            resolve({ message: 'Workspace already removed', workspaces: this._workspaces });
            return;
          }

          this.set(this.META_WORKSPACES, this._workspaces, true)
            .then(() => {
              resolve({ message: 'Workspace has been removed', workspaces: this._workspaces });
            })
            .catch(reject);
        })
        .catch(reject);
    });
    return p;
  }

  public setWorkspaces(data: any): any {
    return this.set(this.META_WORKSPACES, JSON.stringify(data));
  }

  private checkForFavDuplicate(link: string): boolean {
    let index: number = -1;

    for (let i: number = 0; i < this._favs.length; i++) {
      if (this._favs[i].link === link) {
        index = 1;
        break;
      }
    }
    return index >= 0;
  }
  private saveRecents(): void {
    this.storage.user.save(this.META_RECENTS, JSON.stringify(this._links));
  }
}

export default UserData;
