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

/**
 * アクションの属性定義
 */
interface IDateTimeCalcProps{
    verNameGet:string;
    calcOption:string;
    verName:IVarName;
    year:string;
    month:string;
    date:string;
    hour:string;
    minute:string;
    second:string;
}

/**
 *
 */
export class DateTimeCalcAction implements IAction{
    name: string;
    actionProps: IActionProperty[];
    props:IDateTimeCalcProps;
    constructor(){
        this.name="日時を加算／減算する";// DBに登録したアクション名
        this.actionProps=[];
        //プロパティ属性の初期化
        this.props={
            verNameGet:'',
            calcOption:'',
            verName:{name:''},
            year:"0",
            month:"0",
            date:"0",
            hour:"0",
            minute:"0",
            second:"0"
        }
        //アクションを登録する
        this.register();
    }

    /**
     * 基準日となる変数の値が、日付・日時の形式であるか、判断する
     * @param {string} dateValue
     * @returns {boolean}
     */
    isDateValue(dateValue :string){
        let date;

        //正規表現チェック
        let singleDigitMonth = dateValue.match(/(\d{4})-(\d{1})-(\d{1})$/);//4桁の数字-1桁の数字-2桁の数字
        let twoDigitMonth = dateValue.match(/(\d{4})-(\d{2})-(\d{2})$/);//4桁の数字-2桁の数字-2桁の数字
        let singleDigitDate = dateValue.match(/(\d{4})-(\d{2})-(\d{1})$/);//4桁の数字-2桁の数字-1桁の数字
        let twoDigitDate = dateValue.match(/(\d{4})-(\d{1})-(\d{2})$/);//4桁の数字-1桁の数字-2桁の数字
        let dateTimeMilliSecond = dateValue.match(/(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}).(\d{2,3})Z$/);//時刻入りのUTCの日付形式(ミリ秒)
        let dateTime = dateValue.match(/(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})Z$/);//時刻入りのUTCの日付形式

        //date型に変換
        date = new Date(dateValue);

        //date型変換できたか確認
        if(date !== undefined && !isNaN(date.getDate())){
            //正規表現チェック確認
            if(twoDigitMonth === null && singleDigitMonth === null && singleDigitDate === null && twoDigitDate === null && dateTime === null && dateTimeMilliSecond === null){
                throw new Error("計算の基準日となる値が、適切な日付・日時の形式ではありません。「日時を加算/減算する」コンポーネントの処理を中断しました。");
            }
        }
        return true;
    }

    /**
     * 値を数値に変換する
     * @param {any} context
     * @param {string} calcValue,calcOption
     * @returns {number}
     */
    valueToNumber(context :any,calcValue :string,calcOption :string): number{

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

        //計算値が変数の場合は、変数の値を取得
        if(calcOption === "変数" && isNaN(Number(calcValue))){
            calcValue = getContextVarByPath (context.variables,calcValue);
        }

        //数値型に変換
        let number = Number(calcValue);

        //有限数かどうか判定
        if(!isFinite(number)){
            throw new Error("計算値が、数値ではありません。「日時を加算/減算する」コンポーネントの処理を中断しました。");
        }
        return number;
    }

    /**
     * 日付・日時を加算・減算する
     * @param {any} dateValue
     * @param {number} year month day hour minute second
     * @returns {string}
     */
    calcDate(dateValue:any,year:number,month:number,date:number,hour:number,minute:number,second:number):string{

        let calcResult;
        //フィールドの値（文字列）をdate型に変換する
        dateValue = new Date(dateValue);

        // 年を計算
        dateValue.setFullYear(dateValue.getFullYear()+year);
        //月を計算
        dateValue.setMonth(dateValue.getMonth()+month);
        //日を計算
        dateValue.setDate(dateValue.getDate()+date);
        //時間を計算
        dateValue.setHours(dateValue.getHours()+hour);
        //分を計算
        dateValue.setMinutes(dateValue.getMinutes()+minute);
        //秒を計算
        dateValue.setSeconds(dateValue.getSeconds()+second);

        //UTC形式に変換
        calcResult = dateValue.toISOString();
        return calcResult;
    }

    /**
     * アクションの実行を呼び出す
     * @param actionNode
     * @param event
     * @returns
     */
    async process(actionNode:IActionNode,event:any,context:IContext):Promise<IActionResult> {
        let result={
            canNext:true,
            result:false
        };
        try{
            //属性設定を取得する
            this.actionProps = actionNode.actionProps;
            this.props = actionNode.ActionValue as IDateTimeCalcProps;

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

            //基準日となる変数の値取得
            const dateValue = getContextVarByPath (context.variables,this.props.verNameGet);

            //基準日となる変数の値が空の場合、処理を終了する
            if(!dateValue){
                throw new Error("基準値となる変数の値が空、または存在しません。「日時を加算/減算する」コンポーネントの処理を中断しました。");
            }

            let checkDateValue;
            //基準値となる変数の値、日時、日付形式か確認する
            checkDateValue = this.isDateValue(dateValue);

            if(checkDateValue){
                //計算値の入力方法を取得する
                let calcOptions = this.props.calcOption;

                //計算値を数値型に変換する
                let year = this.valueToNumber(context,this.props.year,calcOptions);
                let month = this.valueToNumber(context,this.props.month,calcOptions);
                let date = this.valueToNumber(context,this.props.date,calcOptions);
                let hour = this.valueToNumber(context,this.props.hour,calcOptions);
                let minute = this.valueToNumber(context,this.props.minute,calcOptions);
                let second = this.valueToNumber(context,this.props.second,calcOptions);

                //計算結果の日付を格納する変数
                let calculatedDate;

                //日付を加算、減算する
                calculatedDate = this.calcDate(dateValue,year,month,date,hour,minute,second);

                //計算結果を変数に代入する
                context.variables[this.props.verName.name] = calculatedDate;
            }

            result= {
            canNext:true,
            result:true
            }
            return result;
        }catch(error){
            context.errors.handleError(error,actionNode);
            result.canNext=false;
            return result;
        }

    }

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