package cn.ibizlab.util.aspect;

import lombok.SneakyThrows;
import cn.ibizlab.util.annotation.VersionCheck;
import cn.ibizlab.util.domain.EntityBase;
import cn.ibizlab.util.errors.BadRequestAlertException;
import cn.ibizlab.util.helper.RuleUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.core.annotation.Order;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import java.lang.reflect.Field;
import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Map;

/**
 * 数据库版本检查
 */
@Aspect
@Order(50)
@Component
public class VersionCheckAspect
{
    private final ExpressionParser parser = new SpelExpressionParser();
    private final String IgnoreField = "ignoreversioncheck";

    @SneakyThrows
    @Before("execution(* cn.ibizlab.*.rest.*.update(..)) &&  @annotation(versionCheck)")
    public void BeforeUpdate(JoinPoint point, VersionCheck versionCheck) {
        Object[] args = point.getArgs();
        Object id = args[0];
        Object dto = args[1];
        if(ObjectUtils.isEmpty(id) || ObjectUtils.isEmpty(dto)) {
            return;
        }
        String versionField = versionCheck.versionfield();
        if(StringUtils.isEmpty(versionField)) {
            return;
        }
        versionCheck(versionCheck,point.getTarget(), dto, id);
    }

    @SneakyThrows
    @Before("execution(* cn.ibizlab.*.rest.*.updateBy*(..)) &&  @annotation(versionCheck)")
    public void BeforeUpdateBy(JoinPoint point, VersionCheck versionCheck) {
        Object[] args = point.getArgs();
        if(args.length>=2) {
            Object id = args[args.length-2];
            Object dto = args[args.length-1];
            if(ObjectUtils.isEmpty(id) || ObjectUtils.isEmpty(dto)) {
                return;
            }
            String versionField = versionCheck.versionfield();
            if(StringUtils.isEmpty(versionField)) {
                return;
            }
            versionCheck(versionCheck, point.getTarget(), dto, id);
        }
    }

    private void versionCheck(VersionCheck versionCheck, Object resource, Object dto, Object id) {
        EvaluationContext context = new StandardEvaluationContext();
        context.setVariable("dto", dto);
        //忽略版本检查
        Expression dtoParamsExp = parser.parseExpression("#dto.extensionparams");
        Map dtoParam = dtoParamsExp.getValue(context, Map.class);
        if(!ObjectUtils.isEmpty(dtoParam) && !ObjectUtils.isEmpty(dtoParam.get(IgnoreField)) && dtoParam.get(IgnoreField).equals(1)) {
            return;
        }
        Expression newExp = parser.parseExpression(String.format("#dto.%s", versionCheck.versionfield()));
        Object newVersion = newExp.getValue(context);
        if(ObjectUtils.isEmpty(newVersion)) {
            return;
        }
        //进行版本检查
        Object oldVersion = getDBVersion(versionCheck,getService(resource, versionCheck.entity()), id);
        if(!ObjectUtils.isEmpty(oldVersion)) {
            if(RuleUtils.gt(newVersion, oldVersion)) {
                throw new BadRequestAlertException("数据已变更,可能后台数据已被修改,请重新加载数据", "VersionCheckAspect", "versionCheck");
            }
        }
    }

    /**
     * 获取实体服务对象
     * @param resource
     * @param entity
     * @return
     */
    @SneakyThrows
    private Object getService(Object resource, String entity) {
        Object service = null;
        Field[] fields = resource.getClass().getDeclaredFields();
        for(Field field : fields) {
            if(field.getModifiers()==1 && field.getName().equalsIgnoreCase(String.format("%sService",entity))) {
                service = field.get(resource);
                break;
            }
        }
        return service;
    }

    /**
     * 获取数据库版本
     * @param versionCheck
     * @param service
     * @param id
     * @return
     */
    @SneakyThrows
    private Object getDBVersion(VersionCheck versionCheck, Object service, Object id) {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Timestamp dbVersion = null;
        String versionField = versionCheck.versionfield();
        if(!ObjectUtils.isEmpty(service)) {
            EvaluationContext oldContext = new StandardEvaluationContext();
            oldContext.setVariable("service", service);
            oldContext.setVariable("id", id);
            Expression oldExp = parser.parseExpression("#service.get(#id)");
            EntityBase oldEntity = oldExp.getValue(oldContext, EntityBase.class);
            Object oldDate = oldEntity.get(versionField);
            if(oldDate!=null && oldDate instanceof Timestamp) {
                Timestamp db_time = (Timestamp) oldDate;
                Date db_date = sdf.parse(sdf.format(db_time));
                dbVersion = new Timestamp(db_date.getTime());
            }
        }
        return dbVersion;
    }
}