/**
 * Copyright 2008-2009. Chongqing Communications Industry Services Co.,Ltd Information Technology Branch. All rights reserved.
 * <a>http://www.crunii.com</a>
 */
package com.crunii.micro.domain.common.jdbc.page;

import com.alibaba.druid.pool.DruidDataSource;
import com.crunii.micro.common.constants.DSConstants;
import com.crunii.micro.common.dto.OrderProp;
import com.crunii.micro.common.dto.PageReq;
import com.crunii.micro.common.dto.PageRsp;
import com.crunii.micro.common.exception.BusinessException;
import com.crunii.micro.common.utils.SqlUtil;
import com.crunii.micro.domain.common.jdbc.BaseNPJdbcTemplate;
import com.crunii.micro.domain.common.jdbc.DomainUtil;
import com.crunii.micro.domain.common.jdbc.dto.DomainCol;
import com.crunii.micro.domain.common.jdbc.dto.DomainInfo;
import com.crunii.micro.domain.common.jdbc.page.annotation.Match;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.util.*;

import java.beans.PropertyDescriptor;
import java.lang.annotation.Annotation;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <pre>
 *     单表分页工具类,支持mysql和postgresql
 * </pre>
 *
 * @author 田平 create 2021/11/29 10:03
 */
@Slf4j
public class MyPageHelper {

	public static DruidDataSource getDS() {
		String username = "scheduler";
		String password = "@Scheduler,.123";
		String url = "jdbc:mysql://114.55.37.75:3306/scheduler?useUnicode=true&characterEncoding=utf-8&useSSL=false";
		DruidDataSource ds = new DruidDataSource();
		ds.setUsername(username);
		ds.setPassword(password);
		ds.setUrl(url);
		return ds;
	}

	public static void main(String[] args) throws Exception {
		DruidDataSource ds = getDS();
		BaseNPJdbcTemplate NPJT = new BaseNPJdbcTemplate(ds,DSConstants.DATABASE_MYSQL);
//		PageReq<MonitorLogReq> req = new PageReq<>();
//		MonitorLogReq monitorLogReq = new MonitorLogReq();
//		monitorLogReq.setStartCheckTime(DateUtil.addDay(new Date(), -10));
//		monitorLogReq.setEndCheckTime(new Date());
//		req.setQuery(monitorLogReq);
//		PageRsp<EsMonitorLog> pageRsp = pageEntity(NPJT, EsMonitorLog.class, req);
//		pageRsp.getResult().forEach(System.out::println);

		ds.close();
	}

	public static <T, K> PageRsp<T> pageEntity(BaseNPJdbcTemplate NPJT, Class<T> domainClass, PageReq<K> req) {
		Assert.notNull(req.getQuery(), "请求参数实体不能为空！");

		Class<?> queryClass = req.getQuery().getClass();
		List<OrderProp> orders = req.getOrderProps();
		PropertyDescriptor[] propertyDescriptors = BeanUtils.getPropertyDescriptors(queryClass);
		List<PropertyDescriptor> props = Arrays.stream(propertyDescriptors)
				.filter(p -> p.getWriteMethod() != null).collect(Collectors.toList());

		DomainInfo domainInfo = DomainUtil.getDomainInfoFromCache(domainClass);

		StringBuilder sb = new StringBuilder(domainInfo.getPageBaseSql());
		StringBuilder sbCount = new StringBuilder(domainInfo.getPageCountBaseSql());
		Map<String, Object> params = new HashMap<>();
		boolean appendAnd = false;
		boolean appendAndCount = false;

		for (PropertyDescriptor pd : props) {
			Object value = ReflectionUtils.invokeMethod(pd.getReadMethod(), req.getQuery());
			if (!ObjectUtils.isEmpty(value)) {
				Annotation[] as = ReflectionUtils.findField(queryClass, pd.getName()).getAnnotations();
				Match match = null;
				if (as != null) {
					match = Arrays.stream(as).filter(annotation -> annotation instanceof Match).map(a -> (Match) a).findFirst().orElse(null);
				}
				if (match == null) {
					log.warn("{}.{} not have Match annotation", queryClass.getName(), pd.getName());
					break;
				}
				String propName = null;
				if (value instanceof Collection) {
					if (StringUtils.hasText(match.propName())) {
						propName = match.propName();
					} else {
						throw new BusinessException("{}.{} pageEntity Match.IN type must config propName!", queryClass.getName(), pd.getName());
					}
				} else {
					if (StringUtils.hasText(match.propName())) {
						propName = match.propName();
					} else {
						propName = pd.getName();
					}
				}
				DomainCol col = domainInfo.getCols().get(propName);
				if (col == null) {
					log.warn("{} not have prop:{}", domainInfo.getDomainName(), pd.getName());
					break;
				}
				appendAnd = SqlUtil.appendAnd(sb, appendAnd);
				appendAndCount = SqlUtil.appendAnd(sbCount, appendAndCount);

				String matchSql = getMatchSql(col, match, pd.getName());

				sb.append(matchSql);
				sbCount.append(matchSql);

				params.put(pd.getName(), value);
			}
		}

		//拼接排序字段
		if (!CollectionUtils.isEmpty(orders)) {
			String orderSql = orders.stream().map(orderProp -> {
				DomainCol col = domainInfo.getCols().get(orderProp.getProp());
				if (col != null) {
					return col.getColName() + " " + (orderProp.isAscending() ? "asc" : "desc");
				} else {
					log.warn("{} order prop:{} not find in {}!", queryClass.getName(), orderProp.getProp(), domainInfo.getDomainName());
					return null;
				}
			}).filter(s -> StringUtils.hasText(s)).collect(Collectors.joining(","));
			sb.append(" order by ").append(orderSql);
		}

		String countSql = sbCount.toString();

		log.debug("countSql->>:{}", countSql);
		log.debug("countParams->>:{}", params);
		String dbType = NPJT.getDbType();
		int total = NPJT.queryForInt(countSql, params);
		List<T> list = null;
		if (total > 0) {
			if (dbType.equalsIgnoreCase(DSConstants.DATABASE_MYSQL)) {
				sb.append(" limit :myStart,:pageSize ");
			} else if (dbType.equalsIgnoreCase(DSConstants.DATABASE_POSTGRESQL)) {
				sb.append(" limit :pageSize offset :myStart ");
			} else {
				throw new BusinessException("unsupported this dbType:{}!", dbType);
			}
			params.put("myStart", req.getMyStart());
			params.put("pageSize", req.getPageSize());

			String sql = sb.toString();
			log.debug("pageSql->>:{}", sql);
			log.debug("pageParams->>:{}", params);

			list = NPJT.queryForBeans(domainClass, sql, params);
		} else {
			list = Collections.emptyList();
		}
		PageRsp<T> page = new PageRsp<>();
		page.setPageNo(req.getPageNo());
		page.setPageSize(req.getPageSize());
		page.setTotalNum(total);
		page.setResult(list);
		// 为手机计算是否由下一页
		page.next();

		return page;

	}

	private static String getMatchSql(DomainCol col, Match match, String pdName) {
		StringBuilder sb = new StringBuilder();
		if (Match.EQ.equals(match.condition())) {
			sb.append(" ").append(col.getColName()).append("=:").append(pdName).append(" ");
		} else if (Match.GT.equals(match.condition())) {
			sb.append(" ").append(col.getColName()).append(">:").append(pdName).append(" ");
		} else if (Match.LT.equals(match.condition())) {
			sb.append(" ").append(col.getColName()).append("<:").append(pdName).append(" ");
		} else if (Match.GE.equals(match.condition())) {
			sb.append(" ").append(col.getColName()).append(">=:").append(pdName).append(" ");
		} else if (Match.LE.equals(match.condition())) {
			sb.append(" ").append(col.getColName()).append("<=:").append(pdName).append(" ");
		} else if (Match.LIKE.equals(match.condition())) {
			sb.append(" ").append(col.getColName()).append(" like concat('%',:").append(pdName).append(",'%') ");
		} else if (Match.LEFT_LIKE.equals(match.condition())) {
			sb.append(" ").append(col.getColName()).append(" like concat('',:").append(pdName).append(",'%') ");
		} else if (Match.IN.equals(match.condition())) {
			sb.append(" ").append(col.getColName()).append(" in(:").append(pdName).append(") ");
		} else {
			throw new BusinessException("unsupported this Match.condition:{}", match.condition());
		}

		return sb.toString();
	}

}
