import Promise from "bluebird";
import { IApiService } from "./Api";
import { IStorageService } from "./Storage";
import Helpers, { IHelpers } from "./Helpers";
import { IGridState } from "./Grid4/Grid";
import merge from "deepmerge";

const helpers: IHelpers = new Helpers();

export interface IUserDataService {
  META_HIDE_TUTORIAL: string;
  META_FAVS: string;
  META_BOOKMARKS: 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;
  getBookmarks(): Promise<any>;
  addBookmark(bookmark: IUserDataBookmark): Promise<any>;
  removeBookmark(index: number): Promise<any>;
  saveBookmarks(): Promise<any>;

  // storage data
  getData(key?: string): any;

  getFavs(): Promise<any>;
  getFav(folderId: number, pageId: number): IUserDataFav | undefined;
  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;
  saveFavs(): Promise<any>;

  getRecents(): IUserDataFav[];
  updateRecent(data: IUserDataFav): void;
  addRecent(data: IUserDataFav): void;
  getRecent(folderId: number, pageId: number): IUserDataFav | undefined;
  removeRecent(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 IUserDataBookmark {
  page: string;
  title: string;
  folder: string;
  state?: IGridState | null;
  type: number;
}
export interface IUserDataFavStates {
  name: string;
  state?: IGridState;
}
export interface IUserDataFav {
  id: string;
  link: string;
  title: string;
  customTitle: string;
  pageId?: number;
  folderId?: number;
  folder?: string;
  states?: IUserDataFavStates[];
  pinned?: boolean;
  pinnedOrder?: number;
  pinnedColor?: string;
  view?: string;
  type: number;
  [key: string]: any;
}
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: "%";
}
export interface IUserDataStoragePageSettings {
  [key: string]: IUserDataStoragePageSettingSetting;
}
export interface IUserDataStoragePageSettingSetting {
  autosave?: boolean;
  fit?: string;
  formulaBar?: boolean;
  gridlines?: boolean;
  headings?: boolean;
  highlights?: boolean;
  hoverHighlights?: boolean;
  tracking?: boolean;
}
export interface IUserDataStorageGridState {
  [key: string]: IGridState;
}
export interface IUserDataStorage {
  recent_pages: IUserDataFav[];
  page_settings: IUserDataStoragePageSettings;
  domains_pages_selected: number[];
  domains_pages_pinned_selected: number[];
  color_picker_colors: any;
  grid_state: IUserDataStorageGridState;
  panel_cards: string[];
}

class UserData implements IUserDataService {
  public get META_HIDE_TUTORIAL(): string {
    return "hide_tutorial";
  }
  public get META_FAVS(): string {
    return "shortcuts";
  }
  public get META_BOOKMARKS(): string {
    return "bookmarks";
  }
  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 _bookmarks: IUserDataBookmark[] = [];
  private _links: IUserDataFav[] = [];
  private _workspaces: IUserDataWorkspace[] = [];
  private _storage: IUserDataStorage = {
    recent_pages: [],
    page_settings: {},
    domains_pages_selected: [],
    domains_pages_pinned_selected: [],
    color_picker_colors: {},
    grid_state: {},
    panel_cards: [],
  };
  private _storagePrefix: string = "";

  constructor(
    public api: IApiService,
    public storage: IStorageService,
    public userId: number
  ) {
    this._storagePrefix = `${storage.user.prefix}_data`;
    const localstorage = storage.user.get(this._storagePrefix, {});
    this._storage = merge(this._storage, localstorage);
    this._links = storage.user.get(this.META_RECENTS, []);
    if (this._links.length) {
      this._storage.recent_pages = this._links;
    }
    return;
  }

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

  public get bookmarks() {
    return this._bookmarks;
  }

  // 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.upgradeFavs();
    this._bookmarks = this.get(this.META_BOOKMARKS, [], 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 getRecent(folderId: number, pageId: number): IUserDataFav | undefined {
    let link: string = this.getFavLink(folderId, pageId);
    return this._links.find((f) => {
      return f.link == link;
    });
  }
  public removeRecent(data: IUserDataFav): void {
    let index = this._links.findIndex((f) => {
      return f.link == data.link;
    });
    if (index > -1) this._links.splice(index, 1);
    // 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].id !== data.id) 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,
    folder?: 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 && this._favs[index].folder == folder)
    ) {
      return false;
    }
    this._favs[index].title = title;
    this._favs[index].folderId = folderId;
    this._favs[index].pageId = pageId;
    if (folder) this._favs[index].folder = folder;
    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)) {
          data.id = helpers.getUuid();
          this._favs.push(data);
          this.upgradeFavs();
          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].id === data.id) {
              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 getFav(folderId: number, pageId: number): IUserDataFav | undefined {
    let link: string = this.getFavLink(folderId, pageId);
    return this._favs.find((f) => {
      return f.link == link;
    });
  }
  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));
  }

  // public createBookmark(): IUserDataBookmark {
  //   return {
  //     title: '',
  //     folder: '',
  //     page: '',
  //     state: null,
  //     type: 0
  //   }
  // }
  public getBookmarks(): Promise<any> {
    let p: Promise<any> = new Promise((resolve, reject) => {
      this.refresh()
        .then(() => {
          resolve(this._bookmarks);
        })
        .catch(reject);
    });
    return p;
  }
  public addBookmark(bookmark: IUserDataBookmark): Promise<any> {
    let p: Promise<any> = new Promise((resolve, reject) => {
      // this.refresh()
      //   .then(() => {
      this._bookmarks.push(bookmark);
      this.set(this.META_BOOKMARKS, this._bookmarks, true)
        .then(() => {
          resolve({
            message: "Favourite has been saved",
            bookmarks: this._bookmarks,
          });
        })
        .catch(reject);
      // })
      // .catch(reject);
    });
    return p;
  }
  public removeBookmark(index: number): Promise<any> {
    this._bookmarks.splice(index, 1);
    return this.saveBookmarks();
  }
  public saveBookmarks(): Promise<any> {
    let p: Promise<any> = new Promise((resolve, reject) => {
      this.set(this.META_BOOKMARKS, this._bookmarks, true)
        .then(() => {
          resolve({
            message: "Favourites has been updated",
            bookmarks: this._bookmarks,
          });
        })
        .catch(reject);
    });
    return p;
  }

  public getData(key?: string): any {
    return this._storage;
  }

  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 upgradeFavs(): void {
    for (let i: number = 0; i < this._favs.length; i++) {
      if (this._favs[i].id === undefined) {
        this._favs[i].id = helpers.getUuid();
      }
      ["pinned"].forEach((key) => {
        if (this._favs[i][key] === undefined) this._favs[i][key] = false;
      });
      ["pinnedOrder"].forEach((key) => {
        if (this._favs[i][key] === undefined) this._favs[i][key] = 0;
      });
      ["pinnedColor", "view", "customTitle"].forEach((key) => {
        if (this._favs[i][key] === undefined) this._favs[i][key] = "";
      });
      if (this._favs[i].pageId) continue;
      let domainPage = this._favs[i].link.split("/");
      this._favs[i].folderId = parseInt(domainPage[2]);
      this._favs[i].pageId = parseInt(domainPage[4]);
    }
  }
  private saveRecents(): void {
    this.storage.user.save(this.META_RECENTS, JSON.stringify(this._links));
  }
  private saveData(): void {
    this.storage.user.save(this._storagePrefix, JSON.stringify(this._storage));
  }
}

export default UserData;
