package com.inkasso.adapter.service;


import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.inkasso.adapter.exception.ServiceException;
import com.inkasso.adapter.exception.enums.ExceptionEnum;
import com.inkasso.adapter.page.PageFactory;
import com.inkasso.adapter.page.PageQuery;
import com.inkasso.adapter.page.PageResult;
import com.inkasso.adapter.page.Sort;
import com.inkasso.adapter.response.ResponseData;
import com.inkasso.adapter.response.SuccessResponseData;
import com.inkasso.adapter.service.query.*;
import com.inkasso.adapter.util.BeanUtilsProxy;
import org.apache.commons.lang3.StringUtils;
import org.springframework.util.CollectionUtils;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.util.Collection;
import java.util.List;

/**
 * @author 张超
 * @date 2021/5/11
 */
public class KafubaoServiceImpl<M extends BaseMapper<T>, T, D> extends ServiceImpl<M, T> implements
        KafubaoService<T, D> {


  protected Class<D> dClass;

  public KafubaoServiceImpl() {
    ParameterizedType ptype = (ParameterizedType) this.getClass().getGenericSuperclass();
    dClass = (Class<D>) ptype.getActualTypeArguments()[2];
  }

  /**
   * 查询全部
   *
   * @param obj
   * @return
   */
  @Override
  public List<T> queryList(Sort obj) {

    QueryWrapper wrapper = createQueryWrapper(obj);
    List<T> list = this.baseMapper.selectList(wrapper);
    sort(wrapper, obj);
    return list;
  }

  @Override
  public IPage<T> originalPageQuery(PageQuery obj) {
    QueryWrapper wrapper = createQueryWrapper(obj);

    IPage<T> page = PageFactory.createPage(obj);

    page = this.baseMapper.selectPage(page, wrapper);

    return page;
  }

  /**
   * 分页查询
   *
   * @param obj
   * @return
   */
  @Override
  public ResponseData<PageResult<D>> pageQuery(PageQuery obj) {

    QueryWrapper wrapper = createQueryWrapper(obj);

    IPage<T> page = PageFactory.createPage(obj);
    page = this.baseMapper.selectPage(page, wrapper);
    sort(wrapper, obj);
    List<D> ds = BeanUtilsProxy.copyPropertiesList(page.getRecords(), dClass);
    PageResult pr = new PageResult(page, ds);

    return ResponseData.success(pr);

  }

  @Override
  public <V> ResponseData<PageResult<V>> pageQuery(PageQuery obj, Class<V> clazz) {

    QueryWrapper wrapper = createQueryWrapper(obj);

    IPage<T> page = PageFactory.createPage(obj);
    page = this.baseMapper.selectPage(page, wrapper);
    List<V> vs = BeanUtilsProxy.copyPropertiesList(page.getRecords(), clazz);
    PageResult p = new PageResult(page, vs);

    return SuccessResponseData.success(p);
  }


  /**
   * 创建查询wrapper
   *
   * @param obj
   * @return
   */
  private QueryWrapper createQueryWrapper(Object obj) {
    QueryWrapper<T> wrapper = new QueryWrapper<>();
    if (obj == null) {
      return wrapper;
    }
    Field[] fields = obj.getClass().getDeclaredFields();
    try {
      for (Field f : fields) {
        Annotation[] aArray = f.getDeclaredAnnotations();
        if (aArray.length == 0) {
          continue;
        }
        for (Annotation a : aArray) {
          if (a.annotationType().equals(Equals.class)) {
            Equals equals = f.getAnnotation(Equals.class);
            Method method = obj.getClass()
                .getMethod(getMethodName(f), new Class[0]);
            Object dataObj = method.invoke(obj, new Object[]{});
            if (dataObj != null) {
              String filed = equals.table_filed();
              if (StringUtils.isEmpty(filed)) {
                filed = f.getName();
              }
              wrapper.eq(filed, dataObj);
            }
          }
          if (a.annotationType().equals(LeftLike.class)) {
            LeftLike leftLike = f.getAnnotation(LeftLike.class);

            Method method = obj.getClass()
                .getMethod(getMethodName(f), new Class[0]);
            Object dataObj = method.invoke(obj, new Object[]{});
            if (dataObj != null) {
              String filed = leftLike.table_filed();
              if (StringUtils.isEmpty(filed)) {
                filed = f.getName();
              }
              wrapper.likeLeft(filed, dataObj);
            }
          }
          if (a.annotationType().equals(RightLike.class)) {
            RightLike rightLike = f.getAnnotation(RightLike.class);
            Method method = obj.getClass()
                .getMethod(getMethodName(f), new Class[0]);
            Object dataObj = method.invoke(obj, new Object[]{});
            if (dataObj != null) {
              String filed = rightLike.table_filed();
              if (StringUtils.isEmpty(filed)) {
                filed = f.getName();
              }
              wrapper.likeRight(filed, dataObj);
            }
          }
          //全模糊查询
          if (a.annotationType().equals(Like.class)) {
            Like like = f.getAnnotation(Like.class);

            Method method = obj.getClass()
                .getMethod(getMethodName(f), new Class[0]);
            Object dataObj = method.invoke(obj, new Object[]{});
            if (dataObj != null) {
              String filed = like.table_filed();
              if (StringUtils.isEmpty(filed)) {
                filed = f.getName();
              }
              wrapper.like(filed, dataObj);
            }
          }
          //isNull
          isNull(wrapper, f, a);
          //isNotNull
          isNotNull(wrapper, f, a);
          //In
          in(obj, wrapper, f, a);
          //NotIn
          notIn(obj, wrapper, f, a);
          //between
          between(obj, wrapper, f, a);
        }
      }
    } catch (Exception e) {
      log.error(e.getMessage(), e);
      throw new ServiceException(ExceptionEnum.SYSTEM_ERROR);
    }
    return wrapper;
  }

  private void in(Object obj, QueryWrapper<T> wrapper, Field f, Annotation a)
      throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
    if (a.annotationType().equals(In.class)) {
      In in = f.getAnnotation(In.class);
      Method method = obj.getClass().getMethod(getMethodName(f), new Class[0]);
      Object dataObj = method.invoke(obj, new Object[]{});
      if (dataObj != null) {
        String filed = in.table_filed();
        if (StringUtils.isEmpty(filed)) {
          filed = f.getName();
        }
        if (dataObj.getClass().isArray()) {
          Object[] data = (Object[]) dataObj;
          wrapper.in(filed, data);
        } else if (dataObj instanceof Collection) {
          Collection list = (Collection) dataObj;
          wrapper.in(filed, list);
        } else {
          throw new ServiceException(ExceptionEnum.IN_ANNOTATION_ERROR);
        }
      }
    }
  }

  private void isNotNull(QueryWrapper<T> wrapper, Field f, Annotation a) {
    if (a.annotationType().equals(IsNotNull.class)) {
      IsNotNull isNull = f.getAnnotation(IsNotNull.class);
      String filed = isNull.table_filed();
      if (StringUtils.isEmpty(filed)) {
        filed = f.getName();
      }
      wrapper.isNotNull(filed);
    }
  }

  private void isNull(QueryWrapper<T> wrapper, Field f, Annotation a) {
    if (a.annotationType().equals(IsNull.class)) {
      IsNull isNull = f.getAnnotation(IsNull.class);
      String filed = isNull.table_filed();
      if (StringUtils.isEmpty(filed)) {
        filed = f.getName();
      }
      wrapper.isNull(filed);
    }
  }

  private void between(Object obj, QueryWrapper<T> wrapper, Field f, Annotation a)
      throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
    if (a.annotationType().equals(Between.class)) {

      Between between = f.getAnnotation(Between.class);
      Method method = obj.getClass().getMethod(getMethodName(f), new Class[0]);
      BetweenObj dataObj = (BetweenObj) method.invoke(obj, new Object[]{});
      if (dataObj != null) {
        String filed = between.table_filed();
        if (StringUtils.isEmpty(filed)) {
          filed = f.getName();
        }
        Object start = dataObj.getStart();
        Object end = dataObj.getEnd();
        if (start != null) {
          wrapper.ge(filed, start);
        }
        if (end != null) {
          wrapper.le(filed, end);
        }
      }
    }
  }

  private void notIn(Object obj, QueryWrapper<T> wrapper, Field f, Annotation a)
      throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
    if (a.annotationType().equals(NotIn.class)) {
      NotIn notIn = f.getAnnotation(NotIn.class);
      Method method = obj.getClass().getMethod(getMethodName(f), new Class[0]);
      Object dataObj = method.invoke(obj, new Object[]{});
      if (dataObj != null) {
        String filed = notIn.table_filed();
        if (StringUtils.isEmpty(filed)) {
          filed = f.getName();
        }
        wrapper.notIn(filed, dataObj);
      }
    }
  }


  private String getMethodName(Field f) {
    if (f.getType().getName().equals("boolean")) {
      return "is" + captureName(f.getName());
    } else {
      return "get" + captureName(f.getName());
    }
  }

  private String captureName(String str) {
    // 进行字母的ascii编码前移，效率要高于截取字符串进行转换的操作
    char[] cs = str.toCharArray();
    cs[0] -= 32;
    return String.valueOf(cs);
  }


  private void sort(QueryWrapper<T> wrapper, Sort sort) {
    if (sort == null) {
      return;
    }
    if(sort.getAsc()!=null && !StringUtils.isEmpty(sort.getFiledName())){
        if(sort.getAsc()){
          wrapper.orderByAsc(sort.getFiledName());
        }else{
          wrapper.orderByDesc(sort.getFiledName());
        }
    }
    if (!CollectionUtils.isEmpty(sort.getSortList())) {
      for (Sort s : sort.getSortList()) {
        if (s.getAsc() == true) {
          wrapper.orderByAsc(s.getFiledName());
        } else {
          wrapper.orderByDesc(s.getFiledName());
        }
      }
    }
  }
}
