import { IFunctions } from '../Functions';
import { IPageContent, IPageContentCellIndex, IPageContentProvider } from '../Page/Content';
import { IActionsButton, Button } from './Buttons';
import { IActionsStyle } from './Styles';
import { ITask, ITaskActionSetActionNotification, ITaskActionSetActionShare, ITaskActionSetActionShareComponent, Task, NotificationTaskAction, ComponentTaskAction, ShareTaskAction } from './Tasks';
import Emitter, { IEmitter } from '../Emitter';
import { IApiService, IRequestResult } from '../Api';
import Promise from 'bluebird';
import { IGridCell } from '../Grid3/GridCell';

import Helpers, { IHelpers } from '../Helpers';
import { IActionsState } from './States';
import { IActionsChart } from './Charts';

const helpers: IHelpers = new Helpers();

export interface IActions extends IEmitter {
  EVENT_UPDATED: string;
  buttons: IActionsButton[];
  styles: IActionsStyle[];
  tasks: ITask[];
  freeze: IPageContentCellIndex;
  states: IActionsState[];
  charts: IActionsChart[];
  [key: string]: any;
  parse(str: string): void;
  toString(): string;
  save(): Promise<any>;
  run(gridCell: IGridCell): IActionsRun;
  sendNotification(
    messageNotification: ITaskActionSetActionNotification,
    options?: IActionsSendNotificationOptions
  ): Promise<any>;
  validateButton(button: IActionsButton, buttonIndex: number): string[];
  addButton(button: IActionsButton): IActionsButton[];
  removeButton(name: string): void;
  removeStyle(name: string): void;
  removeTask(name: string): void;
  addChart(chart: IActionsChart): IActionsChart[];
  removeChart(name: string): void;  
}

export interface IActionsRun {
  cellUpdates: IPageContent;
  notifications: ITaskActionSetActionNotification[];
  shares: ITaskActionSetActionShare[];
  styles: IActionsStyle[];
  components: ITaskActionSetActionShareComponent[];
}

export interface IActionsSendNotificationOptions {
  icon?: string;
  type?: string;
}

export class Actions extends Emitter implements IActions {
  public get EVENT_UPDATED(): string {
    return 'updated';
  }
  public buttons: IActionsButton[] = [];
  public styles: IActionsStyle[] = [];
  public tasks: ITask[] = [];
  public states: IActionsState[] = [];
  public freeze: IPageContentCellIndex = {
    row: 0,
    col: 0
  };
  public charts: IActionsChart[] = [];
  // public Content: IPageContentProvider;
  // public Functions: IFunctions;
  constructor(
    public api: IApiService,
    public Content: IPageContentProvider,
    public Functions: IFunctions,
    public folderId: number,
    public pageId: number
  ) {
    super();
  }
  public parse(str: string): void {
    let actions: any = str ? JSON.parse(str) : {};
    ['buttons', 'styles', 'tasks', 'freeze', 'states', 'charts'].forEach(key => {
      if (!actions[key]) return;
      if (key === 'buttons') {
        this.buttons = [];
        actions[key].forEach((buttonData: any) => {
          this.buttons.push(new Button(buttonData));
        });
      } else if (key === 'tasks') {
        this.tasks = [];
        actions[key].forEach((taskData: any) => {
          this.tasks.push(new Task(taskData));
        });
      } else {
        (<IActions>this)[key] = actions[key];
      }
    });
  }
  public toString(): string {
    return JSON.stringify({
      buttons: this.buttons,
      styles: this.styles,
      tasks: this.tasks,
      freeze: this.freeze,
      states: this.states,
      charts: this.charts
    });
  }
  public save(): Promise<any> {
    let p: Promise<any> = new Promise((resolve, reject) => {
      let requestData: any = {
        domainId: this.folderId,
        pageId: this.pageId,
        data: {
          action_definitions: this.toString()
        }
      };
      this.api.savePageSettings(requestData).then(data => {
        this.emit(this.EVENT_UPDATED);
        resolve(data);
      }, reject);
    });
    return p;
  }
  public run(gridCell: IGridCell): IActionsRun {
    let run: IActionsRun = {
      cellUpdates: [],
      notifications: [],
      shares: [],
      styles: [],
      components: []
    };
    let notificationIds: string[] = [];

    this.tasks.forEach(taskGroup => {
      if (!this.Functions.isButtonWithinTaskRange(gridCell, taskGroup)) {
        return;
      }
      taskGroup.tasks.forEach((task, taskIndex) => {

        const cell = this.Content.getCell(gridCell.row, gridCell.col);
        this.Functions.setCellReference(cell);        

        if (!this.Functions.isTaskValid(task)) return;
        task.actions.forEach((action, actionIndex) => {

          const cell = this.Content.getCell(gridCell.row, gridCell.col);
          this.Functions.setCellReference(cell);          

          switch (action.type) {
            case 'component':
              run.components.push(new ComponentTaskAction({
                data: this.Functions.parse(action.component.data),
                type: action.component.type,
                name: action.component.name
              }));
              break;            
            // case 'javascript':
            //   eval(action.javascript.code);
            //   break;            
            case 'value':
              let cellUpdate = this.Functions.updateCell(action);
              if (cellUpdate) {
                this.Content.updateCell(cellUpdate.row, cellUpdate.col, cellUpdate.data);
                if (!run.cellUpdates[cellUpdate.row]) run.cellUpdates[cellUpdate.row] = [];
                run.cellUpdates[cellUpdate.row][cellUpdate.col] = this.Content.getCell(cellUpdate.row, cellUpdate.col);
                this.Functions.updateContentDelta(run.cellUpdates);
              }
              break;
            case 'share':
              run.shares.push(new ShareTaskAction({
                title: this.Functions.parse(action.share.title),
                subTitle: this.Functions.parse(action.share.subTitle),
                blurb: this.Functions.parse(action.share.blurb)
              }));
              break;
            case 'notification':
              const messageNotification: ITaskActionSetActionNotification = new NotificationTaskAction({
                message: this.Functions.parse(action.notification.message, '<br />'),
                body: this.Functions.parse(action.notification.body, '<br />'),
                type: action.notification.type,
                conversation_id: action.notification.conversation_id
              });
              const key: string = `n-${gridCell.row}-${gridCell.col}-${taskIndex}-${actionIndex}`;
              if (notificationIds.indexOf(key) < 0) {
                notificationIds.push(key);
                run.notifications.push(messageNotification);
              }
              break;
          }
        });
      });
    });
    run.styles = this.Functions.getCellFormatting(this.styles, this.buttons);
    return run;
  }
  public sendNotification(
    messageNotification: ITaskActionSetActionNotification,
    options?: IActionsSendNotificationOptions
  ): Promise<any> {
    let body: string = '';
    let icon: string = options && options.icon ? options.icon : '';
    if (messageNotification.type === 'card') {
      body = `<card iconSrc="${icon}" accent="tempo-bg-color--blue"><header>${helpers.safeXmlChars(
        messageNotification.message
      )}</header><body>${helpers.safeXmlChars(messageNotification.body)}</body></card>`;
    } else {
      body = helpers.safeXmlChars(messageNotification.message);
    }
    let type: string = options && options.type ? options.type : 'symphony-obo';

    let p: Promise<any> = new Promise((resolve, reject) => {
      this.api
        .createPageNotification({
          pageId: this.pageId,
          data: {
            type,
            body,
            symphony_conversation_id: messageNotification.conversation_id || ''
          }
        })
        .then(() => {
          resolve(true);
        })
        .catch(err => {
          let message = 'Unable to send message. Notification bot or Conversation ID not found';
          try {
            if (err.code < 500) {
              let data = JSON.parse(err.error);
              message = data.message;
            }
          } catch (e) {}
          reject(message);
        });
    });
    return p;
  }
  public validateButton(button: IActionsButton, buttonIndex: number): string[] {
    let errors = [];
      if (!button.name) {
        errors.push('Name is missing');
      }
      if (!button.range) {
        errors.push('Cell Range is missing');
      }
      if (
        !button.options.length &&
        ['rotate', 'select'].indexOf(button.type) > -1
      ) {
        errors.push('Type Options are missing');
      }
      this.buttons.forEach((existingButton, index) => {
        if (index == buttonIndex) return;
        if (existingButton.name === button.name) {
          errors.push('Name already exists');
        }
      });
      return errors;
  }
  public addButton(button: IActionsButton): IActionsButton[] {
    this.buttons.forEach((b, index) => {
      if (button.name !== b.name) return;
      button.name = button.name + '_' + index;
    });    
    this.buttons.push(button);
    return this.buttons;
  }
  public addChart(chart: IActionsChart): IActionsChart[] {
    this.charts.forEach((b, index) => {
      if (chart.name !== b.name) return;
      chart.name = chart.name + '_' + index;
    });    
    this.charts.push(chart);
    return this.charts;
  }
  public addState(state: IActionsState): IActionsState[] {
    this.states.forEach((s, index) => {
      if (state.name !== s.name) return;
      state.name = state.name + '_' + index;
    });    
    this.states.push(state);
    return this.states;
  }
  public addStyle(style: IActionsStyle): IActionsStyle[] {  
    this.styles.push(style);
    return this.styles;
  }
  public addTask(task: ITask): ITask[] {  
    this.tasks.push(task);
    return this.tasks;
  }
  public removeButton(name: string): void {
    this.buttons.forEach((button, index) => {
      if (button.name !== name) return;
      this.buttons.splice(index, 1);
    });
  }
  public removeState(name: string): void {
    this.states.forEach((state, index) => {
      if (state.name !== name) return;
      this.states.splice(index, 1);
    });
  }
  public removeStyle(name: string): void {
    this.styles.forEach((style, index) => {
      if (style.ref !== name && style.range !== name) return;
      this.styles.splice(index, 1);
    });
  }
  public removeTask(name: string): void {
    this.tasks.forEach((task, index) => {
      if (task.ref !== name && task.range !== name) return;
      this.tasks.splice(index, 1);
    });
  }
  public removeChart(name: string): void {
    this.charts.forEach((chart, index) => {
      if (chart.name !== name) return;
      this.charts.splice(index, 1);
    });
  }
}
