/**
 * アプリ情報
 */
export interface AppInfo {
  appId: string;
  code?: string;
  name: string;
  description?: string;
}

/**
* アクションのプロパティ定義
 */
export interface IActionProperty {
  component: string;
  props: {
    //プロパティ名
    name: string;
    //プロパティ表示名
    displayName: string;
    placeholder: string;
    //プロパティ設定値
    modelValue: any;
  };
}
/**
 * アクションタイプ定義
 */
export interface IActionNode {
  id: string;
  //アクション名
  name: string;
  title: string;
  subTitle: string;
  inputPoint: string;
  //出力ポイント（条件分岐以外未使用）
  outputPoints: Array<string>;
  //ルートアクション（Kintone event）
  isRoot: boolean;
  //アクションのプロパティ定義
  actionProps: Array<IActionProperty>;
  //アクションのプロパティ設定値抽出
  ActionValue: object
  prevNodeId?: string;
  nextNodeIds: Map<string, string>;
}
/**
 * アクションフローの定義
 */
export interface IActionFlow {
  id: string;
  actionNodes: IActionNode[];
  findNodeById(id: string): IActionNode | undefined;
  toJSON(): any;
  getRoot(): IActionNode | undefined;
}

export interface IActionResult{
  canNext:boolean;
  result?:any;
}

/**
 * コンテキスト
 * レコードとフローの変数を持つ
 */
export interface IContext{
  record:any,
  variables:any,
  errors:ErrorManager
}

/**
 * アクションのインターフェース
 */
export interface IAction{
  //アクションの名前（ユーニック名が必要）
  name:string;
  //属性設定情報
  actionProps: Array<IActionProperty>;
  //アクションのプロセス実行関数
  process(prop:IActionNode,event:any,context:IContext):Promise<IActionResult>;
  //アクションの登録関数
  register():void;
}

export interface IField{
  name?:string;
  code:string;
  type?:string;
  required?:boolean;
  options?:string;
  label?: string;
}
//変数のインターフェース
export interface IVarName{
  name:string;
  fields?:IVarName[];
}

/**
 * ユーザー、グループ、組織などオブジェクト類型
 */
export interface ICodeValue {
  code: string;
  name?: string;
}

/**
 * アクションのプロパティ定義に基づいたクラス
 */
export class ActionProperty implements IActionProperty {
  component: string;
  props: {
    // プロパティ名
    name: string;
    // プロパティ表示名
    displayName: string;
    placeholder: string;
    // プロパティ設定値
    modelValue: any;
  };

  static defaultProperty(): IActionProperty {
    return new ActionProperty('InputText', 'displayName', '表示名', '表示を入力してください', '');
  };

  constructor(
    component: string,
    name: string,
    displayName: string,
    placeholder: string,
    modelValue: any
  ) {
    this.component = component;
    this.props = {
      name: name,
      displayName: displayName,
      placeholder: placeholder,
      modelValue: modelValue
    };
  }
}

/**
 * IActionNodeの実装、RootActionNode以外のアクション定義
 */
export class ActionNode implements IActionNode {
  id: string;
  name: string;
  get title(): string {
    const prop = this.actionProps.find((prop) => prop.props.name === "displayName");
    return prop?.props.modelValue;
  };
  get subTitle(): string {
    return this.name;
  };
  inputPoint: string;
  //出力ポイント（条件分岐以外未使用）
  outputPoints: Array<string>;
  actionProps: Array<IActionProperty>;
  get isRoot(): boolean {
    return false;
  };
  get ActionValue(): object {
    const propValue: any = {};
    this.actionProps.forEach((value) => {
      propValue[value.props.name] = value.props.modelValue
    });
    return propValue;
  };
  prevNodeId?: string;
  nextNodeIds: Map<string, string>;
  constructor(
    {id,name,title,inputPoint,outputPoints,actionProps}:
    {
      id:string,
      name: string,
      title: string,
      inputPoint: string,
      outputPoints: Array<string>,
      actionProps: Array<IActionProperty>}
  ) {
    this.id=id;
    this.name = name;
    this.inputPoint = inputPoint;
    this.outputPoints = outputPoints;
    const defProp = ActionProperty.defaultProperty();
    defProp.props.modelValue = title;
    this.actionProps = actionProps;
    this.nextNodeIds = new Map<string, string>();
  }

}
/**
 * ルートアクション定義
 */
export class RootAction implements IActionNode {
  id: string;
  name: string;
  title: string;
  subTitle: string;
  inputPoint: string;
  //出力ポイント（条件分岐以外未使用）
  outputPoints: Array<string>;
  isRoot: boolean;
  actionProps: Array<IActionProperty>;
  ActionValue: object;
  prevNodeId?: string = undefined;
  nextNodeIds: Map<string, string>;
  constructor(
    {id,name,title,subTitle}:{
      id:string,
      name: string,
      title: string,
      subTitle: string}
  ) {
    this.id = id;
    this.name = name;
    this.title = title;
    this.subTitle = subTitle;
    this.inputPoint = '';
    this.outputPoints = [];
    this.isRoot = true;
    this.actionProps = [];
    this.ActionValue = {};
    this.nextNodeIds = new Map<string, string>();
  }
}

/**
 * アクションフローの定義
 */
export class ActionFlow implements IActionFlow {
  id: string;
  actionNodes: Array<IActionNode>;
  constructor(actionNodes: Array<IActionNode> | RootAction) {
    if (actionNodes instanceof Array) {
      this.actionNodes = actionNodes;
    } else {
      this.actionNodes = [actionNodes];
    }
    this.id = '';
  }
  /***
   * IDでActionNodeを取得する
   */
  findNodeById(id: string): IActionNode | undefined {
    return this.actionNodes.find((node) => node.id === id);
  }

  toJSON() {
    return {
      id: this.id,
      actionNodes: this.actionNodes.map(node => {
        const { nextNodeIds, ...rest } = node;
        return {
          ...rest,
          nextNodeIds: Array.from(nextNodeIds.entries())
        };
      })
    };
  }

  getRoot(): IActionNode | undefined {
    return this.actionNodes.find(node => node.isRoot)
  }

  static fromJSON(json: string): ActionFlow {
    const parsedObject = JSON.parse(json);

    const actionNodes = parsedObject.actionNodes.map((node: any) => {
      const nodeClass = !node.isRoot ? new ActionNode(node)
        : new RootAction(node);
      nodeClass.nextNodeIds = new Map<string,string>(Object.entries(node.nextNodeIds));
      nodeClass.prevNodeId = node.prevNodeId;
      nodeClass.id = node.id;
      return nodeClass;
    });
    const actionFlow = new ActionFlow(actionNodes);
    actionFlow.id = parsedObject.id;
    return actionFlow;
  }
}

export interface IActionError{
  action: IActionNode,
  error:string
}

export class ErrorManager{
  private errors:IActionError[]=[];
  
  public get hasError():boolean{
    return this.errors.length>0;
  }

  public handleError(err:any,action:IActionNode,defaultMessage?:string){
    const defMsg = defaultMessage ? defaultMessage : '';
    let error = err instanceof Error? `${defMsg} ${err.message}` : defMsg ;
    console.error(`「${action.name}」処理中例外発生しました。`,err);
    this.errors.push({error,action});
  }

  public setEvent(event:any){
     const messages = this.errors.map((err)=>{
        return `「${err.action.name}」処理中例外発生しました。\n詳細:${err.error}`;
     }); 
     event.error=messages.join('\n');
  }

  public showError(){
    const msg =this.errors.map((err)=>{
      return `「${err.action.name}」処理中例外発生しました。\n詳細:${err.error}`;
    });
    window.alert(msg);
    // const html=
    // `<div id="myalert" class="notifier-cybozu notifier-error-cybozu" style="left: 50%; top: 0px;">
    // <div class="notifier-header-cybozu"><div class="notifier-title-cybozu">エラー</div>
    // <ul class="notifier-body-cybozu"><li>${msg}</li></ul></div>
    // <a class="notifier-remove-cybozu" href="javascript:void(0)"></a></div>`
    // const alert = $(html).appendTo("body");
    // alert.children("#myalert .notifier-remove-cybozu").on("click",(ev)=>{
    //    $("myalert").slideUp().remove();
    // });
  }
}

