package cn.ibizlab.businesscentral.core.util.aspect;

import cn.ibizlab.businesscentral.core.odoo_base.domain.Res_supplier;
import cn.ibizlab.businesscentral.core.odoo_ir.domain.Ir_model_fields;
import cn.ibizlab.businesscentral.core.odoo_ir.domain.Ir_property;
import cn.ibizlab.businesscentral.core.odoo_ir.filter.Ir_propertySearchContext;
import cn.ibizlab.businesscentral.core.odoo_ir.service.IIr_model_fieldsService;
import cn.ibizlab.businesscentral.core.odoo_ir.service.IIr_propertyService;
import cn.ibizlab.businesscentral.core.util.helper.EBSServiceImpl;
import cn.ibizlab.businesscentral.util.annotation.Audit;
import cn.ibizlab.businesscentral.util.annotation.DynaProperty;
import cn.ibizlab.businesscentral.util.domain.EntityBase;
import cn.ibizlab.businesscentral.util.domain.EntityMP;
import cn.ibizlab.businesscentral.util.helper.CachedBeanCopier;
import cn.ibizlab.businesscentral.util.helper.CaseFormatMethod;
import cn.ibizlab.businesscentral.util.helper.DEFieldCacheMap;
import cn.ibizlab.businesscentral.util.security.SpringContextHolder;
import cn.ibizlab.businesscentral.util.service.IBZDataAuditService;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import lombok.SneakyThrows;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
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.NumberUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Field;
import java.sql.Timestamp;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * 动态属性切面
 */
@Aspect
@Component
@EnableAspectJAutoProxy(exposeProxy = true, proxyTargetClass = true)
public class DynaPropertyAspect {
    private final ExpressionParser parser = new SpelExpressionParser();

    @Autowired
    IIr_propertyService ir_propertyService;

    @Autowired
    IIr_model_fieldsService ir_model_fieldsService;

    @AfterReturning(value = "execution(* cn.ibizlab.businesscentral.core.*.service.*.*.get(..)) && args(id)", returning = "entity")
    public void get(Long id, EntityMP entity) {
        Map<String, DynaProperty> dynaPropertyFields = DEFieldCacheMap.getDynaPropertyFields(entity.getClass());
        for (Map.Entry<String, DynaProperty> entry : dynaPropertyFields.entrySet()) {
            Field field = DEFieldCacheMap.getField(entity.getClass(), entry.getKey());
            String fieldProperty = CaseFormatMethod.exec(field.getName(), "lC2l_u");
            DynaProperty dynaProperty = entry.getValue();
            Ir_propertySearchContext ctx = new Ir_propertySearchContext();
            ctx.setN_name_eq(fieldProperty);
            ctx.setN_res_id_eq(String.format("%s,%s", dynaProperty.res_model(), id));
            List<Ir_property> ir_properties = ir_propertyService.searchDefault(ctx).getContent();
            if (ir_properties.size() == 0)
                continue;
            Ir_property property = ir_properties.get(0);
            if (property.getType().equals("many2one")) {
                Long refId = NumberUtils.parseNumber((property.getValueReference().split(","))[1], Long.class);
                entity.set(fieldProperty, refId);
                if (!StringUtils.isEmpty(dynaProperty.pickup_text())) {

                    EntityMP refEntity = getEntity(SpringContextHolder.getBean(dynaProperty.reference()), refId);

                    entity.set(dynaProperty.pickup_text(), refEntity.get(dynaProperty.reference_field()));
                }

            } else if (property.getType().equals("float") || property.getType().equals("monetary")) {
                entity.set(fieldProperty, property.getValueFloat());
            } else if (property.getType().equals("integer")) {
                entity.set(fieldProperty, property.getValueInteger());
            } else if (property.getType().equals("date") || property.getType().equals("datetime")) {
                entity.set(fieldProperty, property.getValueDatetime());
            } else {
                entity.set(fieldProperty, property.getValueText());
            }
        }
    }


    @Around(value = "execution(* cn.ibizlab.businesscentral.core.*.service.*.*.create(..)) ")
    public Object create(ProceedingJoinPoint point) throws Throwable {

        Object[] args = point.getArgs();
        if (ObjectUtils.isEmpty(args) || args.length == 0)
            return point.proceed();

        Object arg = args[0];
        if (arg instanceof EntityBase) {
            EntityBase entity = (EntityBase) arg;
            Map<String, DynaProperty> dynaPropertyFields = DEFieldCacheMap.getDynaPropertyFields(entity.getClass());
            if (dynaPropertyFields.size() == 0)
                return point.proceed();
            Map<String, Object> keyMap = new HashMap<>();
            for (Map.Entry<String, DynaProperty> entry : dynaPropertyFields.entrySet()) {
                Field field = DEFieldCacheMap.getField(entity.getClass(), entry.getKey());
                String fieldProperty = CaseFormatMethod.exec(field.getName(), "lC2l_u");
                if (!StringUtils.isEmpty(entity.get(fieldProperty))) {
                    keyMap.put(fieldProperty, entity.get(fieldProperty));
                }
            }

            point.proceed();

            for (Map.Entry<String, DynaProperty> entry : dynaPropertyFields.entrySet()) {
                Field field = DEFieldCacheMap.getField(entity.getClass(), entry.getKey());
                DynaProperty dynaProperty = entry.getValue();
                String fieldProperty = CaseFormatMethod.exec(field.getName(), "lC2l_u");
                if (keyMap.containsKey(fieldProperty)) {
                    Ir_model_fields ir_model_fields = ir_model_fieldsService.getOne(new QueryWrapper<Ir_model_fields>().eq("name", fieldProperty).eq("model", dynaProperty.res_model()));
                    if (ir_model_fields == null)
                        continue;
                    Ir_property property = new Ir_property();
                    property.setName(fieldProperty);
                    property.setType(ir_model_fields.getTtype());
                    property.setFieldsId(ir_model_fields.getId());
                    property.setResId(String.format("%s,%s", dynaProperty.res_model(), entity.get("id")));
                    if (ir_model_fields.getTtype().equals("many2one")) {
                        Long refId = NumberUtils.parseNumber(keyMap.get(fieldProperty).toString(), Long.class);
                        entity.set(fieldProperty, refId);
                        EBSServiceImpl refServiceImpl = (EBSServiceImpl)SpringContextHolder.getBean(dynaProperty.reference());
                        if (!StringUtils.isEmpty(dynaProperty.pickup_text())) {
                            EntityMP refEntity = getEntity(refServiceImpl, refId);
                            entity.set(dynaProperty.pickup_text(), refEntity.get(dynaProperty.reference_field()));
                        }
                        property.setValueReference(String.format("%s,%s",refServiceImpl.getIrModel(),entity.get(fieldProperty)));
                    } else if (ir_model_fields.getTtype().equals("float") || ir_model_fields.getTtype().equals("monetary")) {
                        property.setValueFloat((Double) keyMap.get(fieldProperty));
                    } else if (ir_model_fields.getTtype().equals("integer")) {
                        property.setValueInteger((Integer) keyMap.get(fieldProperty));
                    } else if (ir_model_fields.getTtype().equals("date") || ir_model_fields.getTtype().equals("datetime")) {
                        property.setValueDatetime((Timestamp) keyMap.get(fieldProperty));
                    } else {
                        property.setValueText(keyMap.get(fieldProperty).toString());
                    }
                    ir_propertyService.create(property);
                }

            }
        }

        return point.proceed();
    }


    @Before("execution(* cn.ibizlab.businesscentral.core.*.service.*.*.update(..)) && args(entity,..)")
    public void update(EntityMP entity) throws Throwable {
        Map<String, DynaProperty> dynaPropertyFields = DEFieldCacheMap.getDynaPropertyFields(entity.getClass());
        if (dynaPropertyFields.size() == 0)
            return;
        for (Map.Entry<String, DynaProperty> entry : dynaPropertyFields.entrySet()) {
            Field field = DEFieldCacheMap.getField(entity.getClass(), entry.getKey());
            DynaProperty dynaProperty = entry.getValue();
            String fieldProperty = CaseFormatMethod.exec(field.getName(), "lC2l_u");
            if (entity.getFocusNull().contains(fieldProperty)) {
                ir_propertyService.getBaseMapper().delete(new QueryWrapper<Ir_property>().eq("name", fieldProperty).eq("res_id", String.format("%s,%s", dynaProperty.res_model(),entity.get("id"))));
                entity.getFocusNull().remove(fieldProperty);
            }else{
                if(StringUtils.isEmpty(entity.get(fieldProperty))){
                    continue;
                }
                Ir_propertySearchContext ctx = new Ir_propertySearchContext();
                ctx.setN_name_eq(fieldProperty);
                ctx.setN_res_id_eq(String.format("%s,%s", dynaProperty.res_model(), entity.get("id")));
                List<Ir_property> ir_properties = ir_propertyService.searchDefault(ctx).getContent();
                if (ir_properties.size() == 0){
                    Ir_model_fields ir_model_fields = ir_model_fieldsService.getOne(new QueryWrapper<Ir_model_fields>().eq("name", fieldProperty).eq("model", dynaProperty.res_model()));
                    if (ir_model_fields == null)
                        continue;
                    Ir_property property = new Ir_property();
                    property.setName(fieldProperty);
                    property.setType(ir_model_fields.getTtype());
                    property.setFieldsId(ir_model_fields.getId());
                    property.setResId(String.format("%s,%s", dynaProperty.res_model(), entity.get("id")));
                    if (ir_model_fields.getTtype().equals("many2one")) {
                        Long refId = NumberUtils.parseNumber(entity.get(fieldProperty).toString(), Long.class);
                        entity.set(fieldProperty, refId);
                        EBSServiceImpl refServiceImpl = (EBSServiceImpl)SpringContextHolder.getBean(dynaProperty.reference());
                        if (!StringUtils.isEmpty(dynaProperty.pickup_text())) {
                            EntityMP refEntity = getEntity(refServiceImpl, refId);
                            entity.set(dynaProperty.pickup_text(), refEntity.get(dynaProperty.reference_field()));
                        }
                        property.setValueReference(String.format("%s,%s",refServiceImpl.getIrModel(),entity.get(fieldProperty)));
                    } else if (ir_model_fields.getTtype().equals("float") || ir_model_fields.getTtype().equals("monetary")) {
                        property.setValueFloat((Double) entity.get(fieldProperty));
                    } else if (ir_model_fields.getTtype().equals("integer")) {
                        property.setValueInteger((Integer) entity.get(fieldProperty));
                    } else if (ir_model_fields.getTtype().equals("date") || ir_model_fields.getTtype().equals("datetime")) {
                        property.setValueDatetime((Timestamp) entity.get(fieldProperty));
                    } else {
                        property.setValueText(entity.get(fieldProperty).toString());
                    }
                    ir_propertyService.create(property);
                    return ;
                }
                Ir_property property = ir_properties.get(0);
                if (property.getType().equals("many2one")) {
                    Long refId = NumberUtils.parseNumber(entity.get(fieldProperty).toString(), Long.class);
                    String oldValue = property.getValueReference();
                    property.setValueReference(String.format("%s,%s",oldValue.substring(0,oldValue.indexOf(",")),entity.get(fieldProperty)));

                } else if (property.getType().equals("float") || property.getType().equals("monetary")) {
                    property.setValueFloat((Double) entity.get(fieldProperty));
                } else if (property.getType().equals("integer")) {
                    property.setValueInteger((Integer) entity.get(fieldProperty));
                } else if (property.getType().equals("date") || property.getType().equals("datetime")) {
                    property.setValueDatetime((Timestamp) entity.get(fieldProperty));
                } else {
                    property.setValueText(entity.get(fieldProperty).toString());
                }
                ir_propertyService.update(property);
            }

        }

        return;
    }


    @AfterReturning("execution(* cn.ibizlab.businesscentral.core.*.*.service.*.remove(..))")
    public void remove(JoinPoint point) throws Throwable {
        EBSServiceImpl serviceImpl = (EBSServiceImpl) point.getTarget();
        EntityMP entity = this.getEntity(serviceImpl, point.getArgs()[0]);
        Map<String, DynaProperty> dynaPropertyFields = DEFieldCacheMap.getDynaPropertyFields(entity.getClass());
        if (dynaPropertyFields.size() == 0)
            return;
        for (Map.Entry<String, DynaProperty> entry : dynaPropertyFields.entrySet()) {
            Field field = DEFieldCacheMap.getField(entity.getClass(), entry.getKey());
            DynaProperty dynaProperty = entry.getValue();
            String fieldProperty = CaseFormatMethod.exec(field.getName(), "lC2l_u");
            ir_propertyService.getBaseMapper().delete(new QueryWrapper<Ir_property>().eq("name", fieldProperty).eq("res_id", String.format("%s,%s", dynaProperty.res_model(), point.getArgs()[0])));
        }
        return;

    }


    private EntityMP getEntity(Object service, Object id) {
        EntityMP entity = null;
        if (!ObjectUtils.isEmpty(service)) {
            EvaluationContext oldContext = new StandardEvaluationContext();
            oldContext.setVariable("service", service);
            oldContext.setVariable("id", id);
            Expression oldExp = parser.parseExpression("#service.get(#id)");
            return oldExp.getValue(oldContext, EntityMP.class);
        }
        return entity;
    }

}