package net.northking.cctp.element.datetime.keywords;

import cn.hutool.core.util.ReUtil;
import net.northking.cctp.element.core.IExecuteContext;
import net.northking.cctp.element.core.annotation.Argument;
import net.northking.cctp.element.core.annotation.Keyword;
import net.northking.cctp.element.core.annotation.Keywords;
import net.northking.cctp.element.core.annotation.Return;
import net.northking.cctp.element.core.type.DataType;
import net.northking.cctp.element.core.type.InputType;
import net.northking.cctp.element.core.type.ParamScope;
import net.northking.cctp.element.datetime.enums.TypeConversionEnum;
import net.northking.cctp.element.datetime.enums.TypeConversionOperateEnum;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.apache.commons.lang3.time.DateUtils;

import java.util.Date;
import java.util.TimeZone;

/**
 * 时间日期组件
 *
 * @author fengxiang.zhang
 * @since 1.0
 */
@Keywords
public class DateTime {

    /**
     * <p>根据不同时区，获取当前时间，东八区为默认时区。</p>
     * Example:
     * <pre>
     * getCurrentDate('timestamp', "GMT+08:00")                      =   1675930188437
     * getCurrentDate('yyyy-MM-dd', "GMT+08:00")                     =   2019-07-03
     * getCurrentDate('yyyy-MM-dd HH:mm:ss', "GMT+08:00")            =   2019-07-03 12:00:01
     * getCurrentDate('yyyy-MM-dd'T'HH:mm:ss.SSSZ', "GMT+08:00")     =   2019-07-03T16:51:23.544+0800
     * </pre>
     *
     * @param format   日期格式
     * @param timeZone 时区
     * @return 日期字符串
     */
    @Keyword(alias = "获取当前时间")
    @Return(name = "result", comment = "日期字符串", type = DataType.STRING)
    public static String getCurrentDate(
            @Argument(name = "format", comment = "日期格式", type = DataType.STRING) String format,
            @Argument(name = "timeZone", comment = "时区", type = DataType.STRING, required = false, defaultValue = "GMT+08:00") String timeZone,
            @Argument(name = "value", comment = "组件返回值", type = DataType.STRING, scope = ParamScope.OUTPUT, defaultValue = "") String value,
            IExecuteContext context) {
        TimeZone tz = TimeZone.getTimeZone(timeZone);
        String result;
        if ("timestamp".equals(format)) {
            result = new Date().getTime() + "";
        } else {
            result = DateFormatUtils.format(new Date(), format, tz);
        }
        save2ContentText(value, context, result);
        return result;
    }

    /**
     * <p>字符串转换成日期。</p>
     * Example:
     * <pre>
     * convertDate("0","2022-12-20","yyyy-MM-dd","yyyy:MM:dd")               =   "2022:12:20"
     * convertDate("1","Sat, 12 Aug 2022 13:30:00 GMT",null,"yyyy:MM:dd")    =   "2022:08:12"
     * </pre>
     *
     * @param type          类型("0"-字符串转日期,"1"-时间戳转日期)
     * @param source        源日期字符串
     * @param sourcePattern 源日期格式
     * @param targetPattern 目标日期格式
     * @return 日期字符串
     */
    @Keyword(alias = "日期格式转换")
    @Return(name = "result", comment = "日期字符串", type = DataType.OBJECT)
    public static String convertDate(
            @Argument(name = "type", comment = "类型", type = DataType.ENUM, enumValues = TypeConversionEnum.class, inputType = InputType.select) String type,
            @Argument(name = "source", comment = "源日期字符串", type = DataType.STRING) String source,
            @Argument(name = "sourcePattern", comment = "源日期格式", type = DataType.STRING) String sourcePattern,
            @Argument(name = "targetPattern", comment = "目标日期格式", type = DataType.OBJECT) String targetPattern,
            @Argument(name = "value", comment = "组件返回值", type = DataType.STRING, scope = ParamScope.OUTPUT, defaultValue = "") String value,
            IExecuteContext context) {
        TypeConversionEnum typeConversionEnum = TypeConversionEnum.from(type);
        String data;
        Date date;
        switch (typeConversionEnum) {
            case String2Date:
                date = parseDate(source, sourcePattern);
                data = DateFormatUtils.format(date, targetPattern);
                break;
            case TimeStamp2Date:
                try {
                    date = new Date(source);
                } catch (Exception e) {
                    throw new IllegalArgumentException("时间戳数据错误:" + source);
                }
                data = DateFormatUtils.format(date, targetPattern);
                break;
            default:
                throw new IllegalArgumentException("未知的类型:" + type);
        }
        save2ContentText(value, context, data);
        return data;
    }


    /**
     * <p>在原有日期对象上加减时间表达式表示的时间, 返回新的日期时间对象</p>
     * <p>时间表达式支持的单位:<p>
     * <p>天表示单位：  "days","day","d"</p>
     * <p>时表示单位：  "hours","hour","h"</p>
     * <p>分表示单位：  "minutes","minute", "mins","min","m"</p>
     * <p>秒表示单位：  "seconds", "second", "secs", "sec", "s"</p>
     * <p>毫秒表示单位："milliseconds", "millisecond", "millis","ms"</p>
     * Example:
     * <pre>
     * addTimeToDate("0","2022-12-20","yyyy-MM-dd","1day 2hours 5mins 30s","yyyy:MM:dd")     = "2022:12:21"
     * addTimeToDate("1","Sat, 12 Aug 2022 13:30:00 GMT",null,"1day 2hours 5mins 30s","yyyy:MM:dd")     = "2022:08:13"
     * </pre>
     *
     * @param type           类型("0"-字符串格式日期做加减操作,"1"-时间戳格式日期做加减操作)
     * @param source         源日期字符串
     * @param sourcePattern  源日期格式
     * @param timeExpression 时间表达式
     * @param targetPattern  目标日期格式
     * @return 日期对象
     */
    @Keyword(alias = "日期加减")
    @Return(name = "result", comment = "日期字符串", type = DataType.OBJECT)
    public static String addTimeToDate(
            @Argument(name = "type", comment = "类型", type = DataType.ENUM, enumValues = TypeConversionOperateEnum.class, inputType = InputType.select) String type,
            @Argument(name = "source", comment = "源日期字符串", type = DataType.STRING) String source,
            @Argument(name = "sourcePattern", comment = "源日期格式", type = DataType.STRING) String sourcePattern,
            @Argument(name = "timeExpression", comment = "时间表达式", type = DataType.STRING) String timeExpression,
            @Argument(name = "targetPattern", comment = "目标日期格式", type = DataType.OBJECT) String targetPattern,
            @Argument(name = "value", comment = "组件返回值", type = DataType.STRING, scope = ParamScope.OUTPUT, defaultValue = "") String value,
            IExecuteContext context) {
        TypeConversionOperateEnum typeConversionEnum = TypeConversionOperateEnum.from(type);
        String data;
        Date date;
        switch (typeConversionEnum) {
            case StringOperate:
                date = parseDate(source, sourcePattern);
                date = addByExpression(date, timeExpression);
                data = DateFormatUtils.format(date, targetPattern);
                break;
            case TimeStampOperate:
                try {
                    date = new Date(source);
                } catch (Exception e) {
                    throw new IllegalArgumentException("时间戳数据错误:" + source);
                }
                date = addByExpression(date, timeExpression);
                data = DateFormatUtils.format(date, targetPattern);
                break;
            default:
                throw new IllegalArgumentException("未知的类型:" + type);
        }
        save2ContentText(value, context, data);
        return data;
    }

    // ##########################################################################################
    // Internal Methods
    // ##########################################################################################

    private static Date parseDate(String source, String sourcePattern) {
        try {
            Date date = DateUtils.parseDate(source, sourcePattern);
            return date;
        } catch (Exception e) {
            throw new IllegalArgumentException("源日期解析失败，请检查源日期格式格式是否匹配");
        }
    }

    private static Date addByExpression(Date date, String timeExpression) {
        timeExpression = normalizeTimeExpression(timeExpression);
        if (timeExpression.length() == 0) {
            return date;
        }
        int sign = 1;
        if (timeExpression.charAt(0) == '-') {
            sign = -1;
            timeExpression = timeExpression.substring(1);
        }
        StringBuilder builder = new StringBuilder();
        for (char c : timeExpression.toCharArray()) {
            switch (c) {
                case 'x':
                    int millis = Integer.parseInt(builder.toString());
                    date = DateUtils.addMilliseconds(date, sign * millis);
                    builder = new StringBuilder();
                    break;
                case 's':
                    int secs = Integer.parseInt(builder.toString());
                    date = DateUtils.addSeconds(date, sign * secs);
                    builder = new StringBuilder();
                    break;
                case 'm':
                    int mins = Integer.parseInt(builder.toString());
                    date = DateUtils.addMinutes(date, sign * mins);
                    builder = new StringBuilder();
                    break;
                case 'h':
                    int hours = Integer.parseInt(builder.toString());
                    date = DateUtils.addHours(date, sign * hours);
                    builder = new StringBuilder();
                    break;
                case 'p':
                    int days = Integer.parseInt(builder.toString());
                    date = DateUtils.addDays(date, sign * days);
                    builder = new StringBuilder();
                    break;
                default:
                    builder.append(c);
            }
        }
        if (builder.length() != 0) {
            throw new IllegalArgumentException("非法的时间表达式:" + timeExpression);
        }
        return date;
    }

    private static String normalizeTimeExpression(String timeExpression) {
        String[] searchList = {" ", "milliseconds", "millisecond", "millis", "seconds", "second", "secs", "sec",
                "minutes", "minute", "mins", "min", "hours", "hour", "days", "day"};
        String[] replacementList = {"", "ms", "ms", "ms", "s", "s", "s", "s", "m", "m", "m", "m", "h", "h", "d", "d"};
        timeExpression = StringUtils.replaceEach(timeExpression.toLowerCase(), searchList, replacementList);
        timeExpression = StringUtils.replaceEach(timeExpression, new String[]{"ms", "d"}, new String[]{"x", "p"});
        return timeExpression;
    }

    public static void save2ContentText(String value, IExecuteContext executeContext, Object s) {
        //将值塞入到组件返回值中
        if (value.contains("#{") && value.contains("}")) {
            String keyValue = ReUtil.get("(?<=\\#\\{)(.+?)(?=\\})", value, 1);
            // #{} 全局变量
            executeContext.setProjectGlobalVariable(keyValue, s.toString());
        }
        if (value.contains("${") && value.contains("}")) {
            String keyValue = ReUtil.get("(?<=\\$\\{)(.+?)(?=\\})", value, 1);
            // ${} 全局变量
            executeContext.setVariable(keyValue, s.toString());
        }
    }
}