import {
  IAction,
  IActionResult,
  IActionNode,
  IActionProperty,
  IContext,
  IField,
} from "../types/ActionTypes";

import { actionAddins } from ".";
import type { Record} from "@kintone/rest-api-client/lib/src/client/types";
import { KintoneAllRecordsError, KintoneRestAPIClient} from "@kintone/rest-api-client";
import "./auto-lookup.scss";
import "bootstrap/js/dist/modal";
// import "bootstrap/js/dist/spinner";
import {Modal} from "bootstrap"
import $ from "jquery";

interface IAutoLookUpProps {
  displayName: string;
  lookupField: LookupField;
  condition: Condition;
}

interface Condition {
  queryString: string;
  index: number;
  type: string;
  children: Child[];
  parent: null;
  logicalOperator: string;
}

interface Child {
  index: number;
  type: string;
  parent: string;
  object: any;
  operator: string;
  value: string;
}

interface LookupField {
  app: App;
  fields: Field[];
}

interface Field {
  name: string;
  type: string;
  code: string;
  label: string;
  noLabel: boolean;
  required: boolean;
  lookup: Lookup;
} 

interface Lookup {
  relatedApp: RelatedApp;
  relatedKeyField: string;
  fieldMappings: FieldMapping[];
  lookupPickerFields: any[];
  filterCond: string;
  sort: string;
}

interface FieldMapping {
  field: string;
  relatedField: string;
}

interface RelatedApp {
  app: string;
  code: string;
}

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

export class AutoLookUpAction implements IAction {
  name: string;
  actionProps: IActionProperty[];
  props: IAutoLookUpProps;
  constructor() {
    this.name = "ルックアップ更新";
    this.actionProps = [];
    this.props = {} as IAutoLookUpProps;
    this.register();
  }

  /***
   * アクセスのメインの処理関数
   */
  async process(
    actionNode: IActionNode,
    event: any,
    context: IContext
  ): Promise<IActionResult> {
    this.actionProps = actionNode.actionProps;
    this.props = {
      ...actionNode.ActionValue,
      condition: JSON.parse((actionNode.ActionValue as any).condition),
    } as IAutoLookUpProps;
    // console.log(context);

    let result = {
      canNext: true,
      result: "",
    } as IActionResult;
    try {
      const lookUpFields = this.props.lookupField.fields.filter(
        (f) => f.lookup && f.lookup.relatedApp.app === String(kintone.app.getId())
      );
      if (!lookUpFields || lookUpFields.length===0) {
        throw new Error(
          `ルックアップの設定は不正です。${this.props.lookupField.fields[0].label} `
        );
      }
      const lookUpField = this.props.lookupField.fields[0]; 
      const key = event.record[lookUpField.lookup.relatedKeyField].value;
      const targetRecords = await this.getUpdateRecords(lookUpField, key);
      //更新対象がない時にスキップ
      if(targetRecords.length===0){
        return result;
      }
      const updateRecords = this.convertForLookup(targetRecords,lookUpField,key);
      console.log("updateRecords", updateRecords);
      this.showSpinnerModel(this.props.lookupField.app,lookUpField);
      const updateResult = await this.updateLookupTarget(updateRecords);
      if(updateResult){
        this.showResult(this.props.lookupField.app,lookUpField,updateRecords.length);
      }
    } catch (error) {
        this.closeDialog();
        context.errors.handleError(error,actionNode,"ルックアップ更新中例外が発生しました");
        result.canNext = false;
    }
    return result;
  }

  /**
   * REST API用クエリ作成
   * TODO:共通関数として作成
   * @param lookUpField 
   * @param key 
   * @returns 
   */
  makeQuery=(lookUpField:Field,key:any)=>{
    let query ="";
    if(typeof key==='number'){
      query = `${lookUpField.code} = ${key}`
    }
    if(typeof key==='string'){
      query = `${lookUpField.code} = "${key}"`
    }
    if(this.props.condition.queryString!==''){
      query = `${query} and (${this.props.condition.queryString})`
    }
    return query;
  }

  /**
   * 更新対象のレコードを取得する
   */
  getUpdateRecords = async (lookUpField:Field,key:any):Promise< Record[]>=>{
    const client=new KintoneRestAPIClient();
    const resp = await client.record.getAllRecords({
      app:this.props.lookupField.app.id,
      fields:["$id"],
      condition:this.makeQuery(lookUpField,key)
    });
    return resp;
  }

 /**
  * ルックアップ更新用レコードに変換する
  * @param targetRecords 更新対象レコード
  * @param lookUpField ルックアップフィールド
  * @param key ルックアップフィールドの値
  * @returns 
  */
  convertForLookup = (targetRecords:Record[],lookUpField:Field,key:any):Array<any>=>{
    return targetRecords.map((r) => ({
      id: Number(r["$id"].value),
      record: { [lookUpField.code]: { value: key } },
    }));
  }

  /**
   * ルックアップ先を更新する
   * @param updateRecords 
   */
  updateLookupTarget = async (updateRecords:Array<any>):Promise<boolean>=>{
    if (updateRecords && updateRecords.length > 0) {
      try{
        const client=new KintoneRestAPIClient();
        const result = await client.record.updateAllRecords({
          app:this.props.lookupField.app.id,
          records:updateRecords
        });
        return true;
      }catch(error ){
        if(error instanceof KintoneAllRecordsError){
          this.showError(this.props.lookupField.app,
                         this.props.lookupField.fields[0],  
                         error as KintoneAllRecordsError,updateRecords.length);
          return false;
        }else{
          throw error;
        } 
      }
      // await kintone.api(kintone.api.url("/k/v1/records.json", true), "PUT", {
      //   app: this.props.lookupField.app.id,
      //   records: updateRecords
      // });
    }
    return false;
  }

  /**
   * 更新中のダイアログ表示
   * @param app 
   */
  showSpinnerModel = (app:App,lookup:Field) => {
    let dialog = $("#alcLookupModal");
    if(dialog.length===0){
      const modalHTML = `<div class="bs-scope">
      <div class="modal" id="alcLookupModal" tabindex="-1" aria-labelledby="staticBackdropLabel" aria-hidden="true">
        <div class="modal-dialog-centered">
          <div class="modal-dialog modal-content">
            <div class="modal-header">
              <h1 class="modal-title fs-5" id="alcLookupModalLabel">ルックアップ同期処理</h1>
              <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
            </div>
            <div class="modal-body">
              <div class="row" id="app${app.id}_${lookup.code}">
                  <div class="spinner-border text-secondary col-1 " role="alert"></div>
                  <div class="col">${app.name}</div>
              </div>
            </div>
            <div class="modal-footer">
              <button type="button" class="btn btn-primary" data-bs-dismiss="modal">OK</button>
            </div>
          </div></div></div></div>`;
      $(modalHTML).appendTo("body");
      dialog = $("#alcLookupModal");
      dialog.get()[0].addEventListener('hidden.bs.modal',(ev)=>{
        Modal.getOrCreateInstance(dialog.get()[0]).dispose();
        $("#alcLookupModal").parent().remove();
      });
    }else{
      const dialogBody=$("#alcLookupModal .modal-body");
      const htmlrow=` 
          <div class="row" id="app${app.id}_${lookup.code}">
            <div class="spinner-border text-secondary col-1 " role="alert">
            </div>
            <div class="col">${app.name}</div>
          <div>`;
      dialogBody.append(htmlrow);
    }
    Modal.getOrCreateInstance(dialog.get()[0]).show(); 
  }

  /**
   * 更新結果を表示する
   * @param app 　更新先アプリ情報
   * @param count 更新件数
   */
  showResult=(app:App,lookup:Field,count:number)=>{
    const dialogBody=$(`#alcLookupModal .modal-body #app${app.id}_${lookup.code}`);
    const html=`    <div class="col-1 text-success">✔</div>
                    <div class="col">${app.name}</div>
                    <div class="col">更新件数:${count}件</div>`;
    dialogBody.html(html);
  }

    /**
   * 更新結果を表示する
   * @param app 　更新先アプリ情報
   * @param count 更新件数
   */
    showError=(app:App,lookup:Field,error:KintoneAllRecordsError,allCount:Number)=>{
      const message=error.error.message;
      const proRecords = error.numOfProcessedRecords;
      const allRecords=error.numOfAllRecords;
      const dialogBody=$(`#alcLookupModal .modal-body #app${app.id}_${lookup.code}`);
      const html=`<div class="col-1 text-danger">✖</div>
                  <div class="col">${app.name}</div>
                  <div class="col">更新件数:${proRecords}/${allRecords}</div>
                  <div class="row text-danger">${message}<div>`;
      dialogBody.html(html);
    }
    /**
     * ダイアログ画面を閉じる
     */
    closeDialog=()=>{
      const dialog = $("#alcLookupModal");
      Modal.getOrCreateInstance(dialog.get()[0]).dispose();
      $("#alcLookupModal").parent().remove();
    }

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

new AutoLookUpAction();