import {
  IAction,
  IActionResult,
  IActionNode,
  IActionProperty,
  IContext,
} from "../types/ActionTypes";
import { actionAddins } from ".";
import {KintoneRestAPIClient} from '@kintone/rest-api-client';
import { Aggregator,Operator} from '../util/aggregates';
import { FieldForm } from "../types/FieldLayout";

interface IDataProcessingProps {
  displayName: string;
  sources: Sources;
  condition: string;
  verName?: VerName;
}

interface Condition {
  queryString: string;
}

interface VerName {
  name: string;
  actionName: string;
  displayName: string;
  vars: Var[];
}

interface Var {
  id: string;
  field: FieldForm;
  logicalOperator: CalcOperator;
  vName: string;
}

interface CalcOperator {
  operator: Operator;
  label: string;
}

interface Sources {
  app: App;
  fields: FieldForm[];
}

interface App {
  id: string;
  name?: string;
}

type Result = {
  [key: string]: {
    type: string;
    value: any[];
  };
};

type AnyObject = {
  [key: string]: any;
};

export class DataProcessingAction implements IAction {
  name: string;
  actionProps: IActionProperty[];
  props: IDataProcessingProps ;

  constructor() {
    this.name = "データ処理";
    this.actionProps = [];
    this.props = {
      displayName:'',
      condition:'',
      sources:{
        app:{
          id:""
        },
        fields:[]
      },
    };
    this.register();
  }

  async process(
    actionNode: IActionNode,
    event: any,
    context: IContext
  ): Promise<IActionResult> {
    this.actionProps = actionNode.actionProps;

    this.props = actionNode.ActionValue as IDataProcessingProps;
    const condition = JSON.parse(this.props.condition) as Condition;
    let result = {
      canNext: true,
      result: "",
    } as IActionResult;
    try {
      if (!this.props) {
        return result;
      }

      const query = this.getQuery(condition.queryString,context.variables);
      const data = await this.selectData(query);
      
      console.log("data ", data);

      if(this.props.verName){
         const varValues= this.props.verName.vars.reduce((acc, f) => {
            const datas=data[f.field.code].value;
            const agg = new Aggregator(datas,f.field);
            const result = agg.calculate(f.logicalOperator.operator)
            acc[f.vName]=result;
            return acc;
          }, {} as AnyObject);
          context.variables[this.props.verName.name]=varValues;
        console.log("context ", context);
      }
      return result;
    } catch (error) {
      context.errors.handleError(error,actionNode);
      result.canNext = false;
      return result;
    }
  }

  /**
   * 
   * @param str 
   * @param vars 
   * @returns 
   */
  getQuery = (str: string, vars: any) => {
    console.log(str);
    const regex = /var\((.*?)\)/g;
    let match;
    while ((match = regex.exec(str)) !== null) {
      const varName = match[1];
      if (varName in vars) {
        str = str.replace(match[0], vars[varName]);
      } else {
        throw new Error(`変数${varName}が見つかりません`);
      }
    }
    console.log(str);
    return str;
  };

  /**
   * データを取得する
   * @param query 
   * @returns 
   */
  selectData = async ( query?: string) => {
    const api = new KintoneRestAPIClient();
    const fields = this.props.sources.fields.map((field)=>field.code);
    const resp =  await api.record.getAllRecords({
        app: this.props.sources.app.id,
        fields:fields,
        condition:query
      });
      const result: Result = {};
      resp.forEach((element) => {
        for (const [key, value] of Object.entries(element)) {
          if (!result[key]) {
            result[key] = { type: value.type, value: [] };
          }
          result[key].value.push(value.value);
        }
      });
      return result;
     
  };

  register(): void {
    actionAddins[this.name] = this;
  }
}

new DataProcessingAction();