package com.nuliji.tools;

import com.github.pagehelper.ISelect;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.nuliji.tools.mybatis.*;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.nuliji.tools.mybatis.*;

import javax.persistence.Column;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import java.util.*;
import java.util.stream.Collectors;

/**
 * Created by gaojie on 2017/11/8.
 */
public abstract class AbstractService {
    protected static final Logger logger = LoggerFactory.getLogger(AbstractService.class);
    protected boolean SOFT_FLAG = false;
    protected <T> int insert(Mapper<T> mapper, T record){
        EntityTable entityTable = MapperHelper.getEntityTable(mapper);
        if(entityTable.countEntityPK() == 1){
            EntityField entityPK = (EntityField) entityTable.getEntityPK().toArray()[0];
            // 添加数据，自增长id需设置为空
            entityPK.set(record, null);
        }
        // 默认行为
        EntityField createTime = entityTable.getEntityProperty("createTime");
        if(createTime != null){
            createTime.set(record, new Date());
        }
        EntityField updateTime = entityTable.getEntityProperty("updateTime");
        if(updateTime != null){
            updateTime.set(record, new Date());
        }
        EntityField deleteTime = entityTable.getEntityProperty("deleteTime");
        if(deleteTime != null){
            deleteTime.set(record, null);
        }
        return mapper.insertSelective(record);
    }
    protected <T> int delete(Mapper<T> mapper, Number id){
        return delete(mapper, id, SOFT_FLAG);
    }

    protected <T> int delete(Mapper<T> mapper, Number id, boolean soft){
        if(soft){
            CommonObject co = CommonObject.getDeleteSoft();
            co.setId(id);
            return mapper.updateByPrimaryKeySelective(Transform.convert(co, MapperHelper.getEntityClass(mapper)));
        }else{
            return mapper.deleteByPrimaryKey(id);
        }
    }

    protected <T> int delete(Mapper<T> mapper, Example example){
        return delete(mapper, example, SOFT_FLAG);
    }

    protected <T> int delete(Mapper<T> mapper, Example example, boolean soft){
        if(soft){
            return mapper.updateByExampleSelective(Transform.convert(CommonObject.getDeleteSoft(), MapperHelper.getEntityClass(mapper)), example);
        }else{
            return mapper.deleteByExample(example);
        }
    }

//    protected <T> int delete(Mapper<T> mapper, T record){
//        return delete(mapper, record, SOFT_FLAG);
//    }
//
//    protected <T> int delete(Mapper<T> mapper, T record, boolean soft){
//        if(soft){
//            // todo
//            return 0;
//        }else{
//            return mapper.delete(record);
//        }
//    }

    protected <T> int update(Mapper<T> mapper, Example example, T record){
        return update(mapper, example, record, SOFT_FLAG);
    }

    protected <T> int update(Mapper<T> mapper, Example example, T record, boolean soft){
        EntityTable entityTable = MapperHelper.getEntityTable(mapper);
        EntityField updateTime = entityTable.getEntityProperty("updateTime");
        if(updateTime != null){
            updateTime.set(record, new Date());
        }
        if(soft){
            softExample(example);
        }
        return mapper.updateByExampleSelective(record, example);
    }

    protected <T> int update(Mapper<T> mapper, T record){
        return update(mapper, record, SOFT_FLAG);
    }

    protected <T> int update(Mapper<T> mapper, T record, boolean soft){
        if(soft){
            Example example = new Example(MapperHelper.getEntityClass(mapper));
            EntityTable entityTable = MapperHelper.getEntityTable(mapper);
            EntityField entityPK = (EntityField) entityTable.getEntityPK().toArray()[0];
            example.and(
                    example.createCriteria()
                            .andEqualTo(entityPK.getName(), entityPK.get(record))
            );
            return update(mapper, example, record, true);
        }else{
            EntityTable entityTable = MapperHelper.getEntityTable(mapper);
            EntityField updateTime = entityTable.getEntityProperty("updateTime");
            if(updateTime != null){
                updateTime.set(record, new Date());
            }
            return mapper.updateByPrimaryKeySelective(record);
        }
    }

    protected <T> List<T> select(Mapper<T> mapper, Example example) {
        return select(mapper, example, SOFT_FLAG);
    }

    protected <T> List<T> select(Mapper<T> mapper, Example example, boolean soft){
        if(soft) softExample(example);
        return mapper.selectByExample(example);
    }

    protected <T> Page<T> select(Mapper<T> mapper, Example example, int page, int pageSize) {
        return select(mapper, example, page, pageSize, SOFT_FLAG);
    }

    protected <T> Page<T> select(Mapper<T> mapper, Example example, int page, int pageSize, boolean soft){
        if(soft) softExample(example);
        boolean count = true;
        if(page == 1 && pageSize == 1) count = false;
        return PageHelper.startPage(page, pageSize, count).doSelectPage(
                () -> mapper.selectByExample(example)
        );
    }

    protected <T> List<T> select(Mapper<T> mapper, T record) {
        return select(mapper, record, SOFT_FLAG);
    }

    protected <T> List<T> select(Mapper<T> mapper, T record, boolean soft){
        if(soft) {
            EntityTable entityTable = MapperHelper.getEntityTable(mapper);
            EntityField deleteStatus = entityTable.getEntityProperty("deleteTime");
            if (deleteStatus != null) {
                deleteStatus.set(record, null);
            }
        }
        return mapper.select(record);
    }

    protected <T> Page<T> select(Mapper<T> mapper, T record, int page, int pageSize) {
        return select(mapper, record, page, pageSize, SOFT_FLAG);
    }

    protected <T> Page<T> select(Mapper<T> mapper, T record, int page, int pageSize, boolean soft){
        if(soft) {
            EntityTable entityTable = MapperHelper.getEntityTable(mapper);
            EntityField deleteStatus = entityTable.getEntityProperty("deleteTime");
            if (deleteStatus != null) {
                deleteStatus.set(record, null);
            }
        }
        return PageHelper.startPage(page, pageSize).doSelectPage(
                () -> mapper.select(record)
        );
    }

    protected <T> T one(Mapper<T> mapper, Number id){
        return one(mapper, id, SOFT_FLAG);
    }
    protected <T> T one(Mapper<T> mapper, Number id, boolean soft) throws RuntimeException {
        if(soft){
            EntityTable entityTable = MapperHelper.getEntityTable(mapper);
            try {
                T record = (T) entityTable.getEntityClass().newInstance();
                EntityField entityPK = (EntityField) entityTable.getEntityPK().toArray()[0];
                entityPK.set(record, id);
                return one(mapper, record, true);
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
            return null;
        }else{
            return mapper.selectByPrimaryKey(id);
        }
    }

    protected <T> T one(Mapper<T> mapper, T record){
        return one(mapper, record, SOFT_FLAG);
    }

    protected <T> T one(Mapper<T> mapper, T record, boolean soft){
        if(soft) {
            EntityTable entityTable = MapperHelper.getEntityTable(mapper);
            EntityField deleteStatus = entityTable.getEntityProperty("deleteTime");
            if (deleteStatus != null) {
                deleteStatus.set(record, null);
            }
        }
        return mapper.selectOne(record);
    }

    protected <T> T one(Mapper<T> mapper, Example example){
        return one(mapper, example, SOFT_FLAG);
    }

    protected <T> T one(Mapper<T> mapper, Example example, boolean soft){
        Page<T> l = select(mapper, example, 1, 1, soft);
//        logger.debug("{},{}", l.getTotal(), l.get(0));
        return l.size() > 0 ? l.get(0) : null;
    }

    protected <T> List<T> select(Mapper<T> mapper){
        return select(mapper, SOFT_FLAG);
    }

    protected <T> List<T> select(Mapper<T> mapper, boolean soft){
        if(soft){
            Example example = new Example(MapperHelper.getEntityClass(mapper));
            return select(mapper, example, true);
        }else{
            return mapper.selectAll();
        }
    }
    protected <T> Page<T> select(Mapper<T> mapper, int page, int pageSize){
        return select(mapper, page, pageSize, SOFT_FLAG);
    }
    protected <T> Page<T> select(Mapper<T> mapper, int page, int pageSize, boolean soft){
        if(soft){
            Example example = new Example(MapperHelper.getEntityClass(mapper));
            return select(mapper, example, page, pageSize, true);
        }else{
            return PageHelper.startPage(page, pageSize).doSelectPage(
                    () -> mapper.selectAll()
            );
        }
    }

    protected <T> List<T> select(Mapper<T> mapper, String ids){
        return select(mapper, ids, SOFT_FLAG);
    }

    protected <T> List<T> select(Mapper<T> mapper, String ids, boolean soft){
        if(soft){
            return select(mapper, Arrays.asList(ids.split(",")), soft);
        }else{
            return mapper.selectByIds(ids);
        }
    }

    protected <T> List<T> select(Mapper<T> mapper, Object[] ids){
        return select(mapper, Arrays.stream(ids).collect(Collectors.toList()), SOFT_FLAG);
    }

    protected <T> List<T> select(Mapper<T> mapper, Object[] ids, boolean soft){
        return select(mapper, Arrays.stream(ids).collect(Collectors.toList()), soft);
    }

    protected <T> List<T> select(Mapper<T> mapper, Collection ids){
        return select(mapper, ids, SOFT_FLAG);
    }

    protected <T> List<T> select(Mapper<T> mapper, Collection ids, boolean soft){
        if(ids == null ) return new ArrayList<T>();
        ids.removeIf((id)->{
            return id == null;
        });
        if(ids.size() == 0) return new ArrayList<T>();
        if(soft){
            Example example = new Example(MapperHelper.getEntityClass(mapper));
            EntityTable entityTable = MapperHelper.getEntityTable(mapper);
            EntityField entityPK = (EntityField) entityTable.getEntityPK().toArray()[0];
            example.and(
                    example.createCriteria()
                    .andIn(entityPK.getName(), ids)
            );
            return select(mapper, example, true);
        }else{
            return mapper.selectByIds(StringUtils.join(ids.toArray(), ','));
        }
    }

    protected <T> int count(Mapper<T> mapper, Example example){
        return count(mapper, example, SOFT_FLAG);
    }

    protected <T> int count(Mapper<T> mapper, Example example, boolean soft){
        if(soft){
            softExample(example);
        }
        return mapper.selectCountByExample(example);
    }

    protected <T> int count(Mapper<T> mapper, T record){
        return count(mapper, record, SOFT_FLAG);
    }

    protected <T> int count(Mapper<T> mapper, T record, boolean soft){
        if(soft){
            EntityTable entityTable = MapperHelper.getEntityTable(mapper);
            EntityField deleteStatus = entityTable.getEntityProperty("deleteTime");
            if(deleteStatus != null){
                deleteStatus.set(record, null);
            }
        }
        return mapper.selectCount(record);
    }

    protected <T> Page<T> page(ISelect select, int page, int pageSize){
        return PageHelper.startPage(page, pageSize).doSelectPage(select);
    }

    private void softExample(Example example){
        example.and(
                example.createCriteria()
                        .andEqualTo("deleteTime", null)
        );
    }

    public static class CommonObject{
        @Id
        @Column(name = "`id`")
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Number id;

        @Column(name = "`create_time`")
        private Date createTime;

        @Column(name = "`update_time`")
        private Date updateTime;

        /**
         * 删除时间(通用)
         */
        @Column(name = "`delete_time`")
        private Date deleteTime;

        public CommonObject(){
        }
        public static CommonObject getDeleteSoft(){
            CommonObject co = new CommonObject();
            co.setDeleteTime(new Date());
            co.setUpdateTime(new Date());
            return co;
        }

        public Number getId() {
            return id;
        }

        public void setId(Number id) {
            this.id = id;
        }

        public Date getCreateTime() {
            return createTime;
        }

        public void setCreateTime(Date createTime) {
            this.createTime = createTime;
        }

        public Date getUpdateTime() {
            return updateTime;
        }

        public void setUpdateTime(Date updateTime) {
            this.updateTime = updateTime;
        }

        public Date getDeleteTime() {
            return deleteTime;
        }

        public void setDeleteTime(Date deleteTime) {
            this.deleteTime = deleteTime;
        }
    }

    public <T> Collection getIds(Collection<T> list, Class<?> clazz, String idProp) throws RuntimeException {
        return Transform.getIds(list, clazz, idProp);
    }

    public <T> Map getMap(Collection<T> list, Class<?> clazz, String keyProp, String valueProp) throws RuntimeException{
        return Transform.getMap(list, clazz, keyProp, valueProp);
    }
}
