import {
  IAction,
  IActionResult,
  IActionNode,
  IActionProperty,
  IContext,
} from "../types/ActionTypes";
import { actionAddins } from ".";
import { KintoneRestAPIClient } from "@kintone/rest-api-client";
import { Lookup } from "@kintone/rest-api-client/lib/src/KintoneFields/types/property";
import { FieldForm, FieldType } from "../types/FieldLayout";
interface Props {
  displayName: string;
  sources: Sources;
  dataMapping: DataMapping;
}

interface DataMapping {
  data: Mapping[];
  createWithNull: boolean;
}

interface Mapping {
  id: string;
  from: From;
  to: To;
  isKey: boolean;
}



interface To {
  app: App;
  fields:FieldForm[];
  isDialogVisible: boolean;
}


interface From {
  sharedText: string;
  id: string;
  objectType: 'variable'|'field'|'text';
}

interface IVar extends From{
  name:{
    name:string;
  }
}

interface IFromField extends From,FieldForm{

}
interface Sources {
  app: App;
}

interface App {
  id: string;
  name: string;
  description: string;
  createdate: string;
}

export class DataUpdateAction implements IAction {
  name: string;
  actionProps: IActionProperty[];
  dataMappingProps: Props;
  constructor() {
    this.name = "データ更新";
    this.actionProps = [];
    this.dataMappingProps = {} as Props;
    this.register();
  }

  async process(
    actionNode: IActionNode,
    event: any,
    context: IContext
  ): Promise<IActionResult> {
    this.actionProps = actionNode.actionProps;
    this.dataMappingProps = actionNode.ActionValue as Props;
    console.log(context);
    let result = {
      canNext: true,
      result: "",
    } as IActionResult;
    try {
      const lookupFixedFieldCodes = await getLookupFixedFieldCodes(
        this.dataMappingProps.sources.app.id
      );

      // createWithNull が有効な場合は、4 番目のパラメーターを true にして doUpdate 関数ブランチを実行します。
      if (this.dataMappingProps.dataMapping.createWithNull) {
        await doUpdate(
          this.dataMappingProps.dataMapping.data,
          this.dataMappingProps.sources.app.id,
          context,
          true, // キーがない場合、またはキーでターゲットが見つからない場合に、マッピング条件によって新しいレコードを作成するかどうかを決定するために使用されます。
          lookupFixedFieldCodes
        );
      } else if (
        // キーがないと更新対象を取得できないため、この時点でのみ更新が行われます。 doUpdate 関数の 4 番目のパラメーターは false です。
        this.dataMappingProps.dataMapping.data
          .map((m) => m.isKey)
          .find((isKey) => isKey === true)
      ) {
        await doUpdate(
          this.dataMappingProps.dataMapping.data,
          this.dataMappingProps.sources.app.id,
          context,
          false,
          lookupFixedFieldCodes
        );
      } else {
        await doCreate(
          this.dataMappingProps.dataMapping.data,
          this.dataMappingProps.sources.app.id,
          context,
          lookupFixedFieldCodes
        );
      }
    } catch (error) {
      context.errors.handleError(error,actionNode);
      result.canNext = false;
    }
    console.log("dataMappingProps", this.dataMappingProps);

    return result;
  }

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

new DataUpdateAction();

const getContextVarByPath = (obj: any, path: string) => {
  return path.split(".").reduce((o, k) => (o || {})[k], obj);
};

interface UpdateRecord {
  id: string;
  record: {
    [key: string]: {
      value: any;
    };
  };
}

const client = new KintoneRestAPIClient();

const getFromValue=(item:Mapping,context:IContext)=>{
  if (item.from.objectType === "variable") {
    const rfrom =item.from as IVar;
    return  getContextVarByPath(context.variables,rfrom.name.name);
  }else if(item.from.objectType === "field"){
    const field = item.from as IFromField;
    return context.record[field.code].value;
  }
  else {
    return item.from.sharedText;
  }
}

const doUpdate = async (
  mappingData: Mapping[],
  appId: string,
  context: IContext,
  needCreate: boolean,
  lookupFixedFieldCodes: string[]
) => {
  const targetField = await findUpdateField(mappingData, appId, context);
  console.log(targetField);
  if (targetField.records.length === 0 && needCreate) {
    await doCreate(mappingData, appId, context, lookupFixedFieldCodes);
  } else {
    // マッピングデータを単純なオブジェクトに処理し、ソース値が変数の場合は変数を置き換えます。
    const mappingRules = mappingData
      .filter(
        (m) =>
          Object.keys(m.from).length > 0 &&
          !lookupFixedFieldCodes.includes(m.to.fields[0].code)
      )
      .map((m) => {
        if (m.from.objectType === "variable") {
          const rfrom =m.from as IVar;
          return {
            value: getContextVarByPath(context.variables,rfrom.name.name),
            code: m.to.fields[0].code,
          };
        }else if(m.from.objectType === "field"){
          const field = m.from as IFromField;
          return {
            value: context.record[field.code].value,
            code: m.to.fields[0].code,
          }
        }
         else {
          return {
            value: m.from.sharedText,
            code: m.to.fields[0].code,
          };
        }
      });

    const updateRecords: UpdateRecord[] = targetField.records.map(
      (targetRecord) => {
        const updateRecord: UpdateRecord["record"] = {};

        // マッピング内のルールにヒットしたフィールドのみが更新されます。
        for (const mapping of mappingRules) {
          if (targetRecord[mapping.code]) {
            updateRecord[mapping.code] = {
              value: mapping.value,
            };
          }
        }

        return {
          id: targetRecord.$id.value as string,
          record: updateRecord,
        };
      }
    );

    console.log(updateRecords);
    await client.record.updateRecords({
      app: appId,
      records: updateRecords,
    });
  }
};

const makeQuery=(field:FieldForm,key:any)=>{
  if(field.type===FieldType.NUMBER || field.type===FieldType.RECORD_NUMBER){
    return `${field.code} = ${Number(key)}`
  }
  if(typeof key==='string'){
    return `${field.code} = "${key}"`
  }
}


const findUpdateField = async (
  mappingData: Mapping[],
  appId: string,
  context: IContext
) => {
  const queryStr = mappingData
    .filter((m) => m.to.app && m.to.fields && m.to.fields.length > 0 && m.isKey)
    .map((m) => {
      if (m.from.objectType === "variable") {
        const vfrom = m.from as IVar;
        return makeQuery(m.to.fields[0],getContextVarByPath(context.variables , vfrom.name.name));
      }
      else if(m.from.objectType === "field"){
        const field = m.from as IFromField;
        return makeQuery(m.to.fields[0],context.record[field.code].value);
      } 
      else{
        return makeQuery(m.to.fields[0],m.from.sharedText);
      }
    })
    .join("&");
  // 検索条件が空の場合は全レコードを返すため、検索対象が見つからない場合は検索は行われません。
  if (queryStr.length === 0) {
    return {
      records: [],
    };
  } else {
    return await client.record.getRecords({
      app: appId,
      // query: undefined
      query: queryStr,
    });
  }
};

const doCreate = async (
  mappingData: Mapping[],
  appId: string,
  context: IContext,
  lookupFixedFieldCodes: string[]
) => {
  const filterHandler =  (item:Mapping)=>{
    if(!item.to.fields || item.to.fields.length===0){
      return false;
    }
    if(item.from.objectType === "variable" && (item.from as IVar).name.name  ){
      return true;
    }
    if(item.from.objectType === "field" && (item.from as IFromField).code){
      return true;
    }
    if(item.from.objectType === "text" && item.from.sharedText!==null){
      return true;
    }
    return false;
  }
  const record = mappingData
    .filter(filterHandler)
    .filter((item) => !lookupFixedFieldCodes.includes(item.to.fields[0].code))
    .reduce((accumulator, item) => {
      return {
        ...accumulator,
        [item.to.fields[0].code]: {
          value: getFromValue(item,context),
        },
      };
    }, {});
  if (record && Object.keys(record).length > 0) {
    console.log(record);
    await client.record.addRecord({
      app:appId,
      record:record
    });
    // await kintone.api(kintone.api.url("/k/v1/record.json", true), "POST", {
    //   app: appId,
    //   record: record,
    // });
  }
};

const getLookupFixedFieldCodes = async (appId: string) => {
  return await client.app
    .getFormFields({ app: appId })
    .then((resp) =>
      Object.values(resp.properties)
        .filter((f) => (f as Lookup).lookup !== undefined)
        .flatMap((f) => (f as Lookup).lookup.fieldMappings.map((m) => m.field))
    );
};
