import { IFunctions } from "../Functions";
import {
  IPageContent,
  IPageContentCellIndex,
  IPageContentProvider,
} from "../Page/Content";
import { IActionsButton, Button } from "./Buttons";
import { IActionsStyle, Style } 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 "../Grid4/GridCell";

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

const helpers: IHelpers = new Helpers();

export interface IActionsFilters {
  enabled: boolean;
}

export interface IActionsConfig {
  states_menu: string;
}

export interface IActions extends IEmitter {
  EVENT_UPDATED: string;
  buttons: IActionsButton[];
  styles: IActionsStyle[];
  tasks: ITask[];
  freeze: IPageContentCellIndex;
  states: IActionsState[];
  charts: IActionsChart[];
  filters: IActionsFilters;
  config: IActionsConfig;
  [key: string]: any;
  parse(str: string): void;
  toString(): string;
  save(): Promise<any>;
  run(gridCell: IGridCell, canEdit?: boolean): 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[];
  values: string[];
  components: ITaskActionSetActionShareComponent[];
}

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

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 filters: IActionsFilters = {
    enabled: false,
  };
  public config: IActionsConfig = {
    states_menu: "",
  };
  // 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 {
    this.reset();
    let actions: any = str ? JSON.parse(str) : {};
    [
      "buttons",
      "styles",
      "tasks",
      "freeze",
      "states",
      "charts",
      "filters",
      "config",
    ].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 === "styles") {
        // this.styles = [];
        actions[key].forEach((data: any) => {
          this.styles.push(new Style(data));
        });
      } else if (key === "tasks") {
        // this.tasks = [];
        actions[key].forEach((taskData: any) => {
          this.tasks.push(new Task(taskData));
        });
      } else if (key === "config") {
        this.config = { ...this.config, ...actions[key] };
      } 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,
      filters: this.filters,
      config: this.config,
    });
  }
  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, canEdit?: boolean): IActionsRun {
    let run: IActionsRun = {
      cellUpdates: [],
      notifications: [],
      shares: [],
      styles: [],
      components: [],
      values: [],
    };
    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 "copy":
              let value: string | number = this.Functions.parse(
                action.data.value
              );
              run.values.push(`${value}`);
              break;
            case "value":
              let cellUpdate = this.Functions.updateCell(action);
              if (cellUpdate && canEdit) {
                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":
              let data: any = {
                type: action.notification.type,
                send_to: action.notification.send_to,
                allow_custom_text: action.notification.allow_custom_text,
                message: this.Functions.parse(
                  action.notification.message,
                  "<br />"
                ),
                body: this.Functions.parse(action.notification.body, "<br />"),
                destinations: {
                  "symphony-obo": []
                },
                notification_destinations: []
              };
              if (action.notification.destinations instanceof Array) {
                action.notification.destinations.forEach((destination) => {
                  if (typeof destination == 'string') data.destinations['symphony-obo'].push(destination);
                  else data.notification_destinations.push(destination);
                })
              }
              const messageNotification: ITaskActionSetActionNotification = new NotificationTaskAction(
                data
              );
              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;
  }

  private reset(): void {
    this.buttons = [];
    this.styles = [];
    this.tasks = [];
    this.config.states_menu = '';
    this.charts = [];
    this.freeze.col = 0;
    this.freeze.row = 0;
    this.filters.enabled = false;
  }

/**
 * 
 * @param messageNotification 
 * @param options 
 * 
 */  

  public sendNotification(
    messageNotification: ITaskActionSetActionNotification,
    options?: IActionsSendNotificationOptions
  ): Promise<any> {
    let message: string = "";
    let icon: string = options && options.icon ? options.icon : "";
    if (messageNotification.type === "card") {
      message = `<card iconSrc="${icon}" accent="tempo-bg-color--blue"><header>${helpers.safeXmlChars(
        messageNotification.message
      )}</header><body>${helpers.safeXmlChars(
        messageNotification.body
      )}</body></card>`;
    } else {
      message = helpers.safeXmlChars(messageNotification.message);
    }
    let type: string = messageNotification.conversation_id
      ? "symphony-obo"
      : "";    
    let data: any = {
      destinations: messageNotification.destinations,
      notification_destinations: messageNotification.notification_destinations,
      message,
    };
    if (type) {
      data.type = type;
    }
    // TODO: move to a list of 'destinations'
    if (messageNotification.conversation_id) {
      data.symphony_conversation_id = messageNotification.conversation_id;
    }
    // if (messageNotification.destinations) {
    //   data.notification_destinations = messageNotification.destinations;
    // }

    const create =
      options && options.write_access
        ? "createPageNotification"
        : "createNotification";

    let p: Promise<any> = new Promise((resolve, reject) => {
      this.api[create]({
        pageId: this.pageId,
        data,
      })
        .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);
    this.emit(this.EVENT_UPDATED);
    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);
    this.emit(this.EVENT_UPDATED);
    return this.styles;
  }
  public addTask(task: ITask): ITask[] {
    this.tasks.push(task);
    this.emit(this.EVENT_UPDATED);
    return this.tasks;
  }
  public removeButton(name: string): void {
    this.buttons.forEach((button, index) => {
      if (button.name !== name) return;
      this.buttons.splice(index, 1);
    });
    this.emit(this.EVENT_UPDATED);
  }
  public removeState(name: string): void {
    this.states.forEach((state, index) => {
      if (state.name !== name) return;
      this.states.splice(index, 1);
    });
    this.emit(this.EVENT_UPDATED);
  }
  public removeStyle(name: string): void {
    this.styles.forEach((style, index) => {
      if (style.ref !== name && style.range !== name) return;
      this.styles.splice(index, 1);
    });
    this.emit(this.EVENT_UPDATED);
  }
  public removeTask(name: string): void {
    this.tasks.forEach((task, index) => {
      if (task.ref !== name && task.range !== name) return;
      this.tasks.splice(index, 1);
    });
    this.emit(this.EVENT_UPDATED);
  }
  public removeChart(name: string): void {
    this.charts.forEach((chart, index) => {
      if (chart.name !== name) return;
      this.charts.splice(index, 1);
    });
  }
}
