/**
 * 
 */
package org.springblade.etl.service;

import cn.hutool.core.thread.NamedThreadFactory;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springblade.core.secure.utils.AuthUtil;
import org.springblade.core.tool.exception.ServiceException;
import org.springblade.core.tool.utils.CollectionUtil;
import org.springblade.etl.dto.SqlJobDto;
import org.springblade.etl.engine.sql.SqlJobThread;
import org.springblade.etl.entity.SqlJob;
import org.springblade.etl.entity.SqlJobInstance;
import org.springblade.etl.entity.SqlJobPermission;
import org.springblade.etl.entity.SqlJobScript;
import org.springblade.etl.entity.bi.ReportDatasource;
import org.springblade.etl.enums.InstanceRunType;
import org.springblade.etl.enums.InstanceStatus;
import org.springblade.etl.enums.JobStatus;
import org.springblade.etl.enums.SqlScriptType;
import org.springblade.etl.mapper.SqlJobMapper;
import org.springblade.etl.service.bi.ReportDatasourceService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 *	SQL任务 服务处理
 *
 * @author aokunsang
 * @date   2023-12-03
 */
@Service
public class SqlJobService extends ServiceImpl<SqlJobMapper, SqlJob>{
	
	@Autowired
	private SqlJobScriptService sqlJobScriptService;
	
	@Autowired
	private SqlJobPermissionService sqlJobPermissionService;
	
	@Autowired
	private SqlJobInstanceService sqlJobInstanceService;
	
	@Autowired
	private ReportDatasourceService reportDatasourceService;
	
	/**
	 * @param userId
	 * @return
	 */
	public List<Integer> categoryIdListOfPerm(Long userId){
		return baseMapper.categoryIdListOfPerm(userId);
	}
	
	/**
	 * 
	 * @param userId
	 * @return
	 */
	public List<Integer> jobIdListOfPerm(Long userId){
		return baseMapper.jobIdListOfPerm(userId);
	}
	
	/**
	 * 
	 * @param jobId
	 * @return
	 */
	@Transactional
	public boolean doRemove(Integer jobId) {
		boolean ret = removeById(jobId);
		if(ret) {
			sqlJobScriptService.remove(Wrappers.<SqlJobScript>lambdaQuery().eq(SqlJobScript::getJobId, jobId));
		}
		return ret;
	}
	
	@Transactional
	public Integer doSaveOrUpdate(SqlJobDto sqlJobDto) {
		SqlJob sqlJob = sqlJobDto.getJob();
		List<Integer> dataPermIdList = sqlJobDto.getDataPermIdList();
		List<SqlJobScript> sqlScriptList = sqlJobDto.getSqlScriptList();
		if(sqlJob.getId() == null) {
			sqlJob.setCreateTime(LocalDateTime.now());
			sqlJob.setCreateBy(AuthUtil.getNickName());
			sqlJob.setCreateUserId(AuthUtil.getUserId());
		}else {
			SqlJob dbJob = this.getById(sqlJob.getId());
			if(dbJob.getJobStatus() == JobStatus.ONLINE) {
				throw new ServiceException("任务需要下线后才能修改");
			}
			sqlJob.setUpdateBy(AuthUtil.getNickName());
			sqlJob.setUpdateUserId(AuthUtil.getUserId());
			sqlJob.setUpdateTime(LocalDateTime.now());
			sqlJobScriptService.remove(Wrappers.<SqlJobScript>lambdaQuery().eq(SqlJobScript::getJobId, sqlJob.getId()));
		}
		if(sqlJob.getJobStatus() == org.springblade.etl.enums.JobStatus.ONLINE_RUN) {
			sqlJob.setJobStatus(org.springblade.etl.enums.JobStatus.ONLINE);
		}
		saveOrUpdate(sqlJob);
		Integer jobId = sqlJob.getId();
		for(SqlJobScript jobScript : sqlScriptList) {
			jobScript.setJobId(jobId);
			//判断sql是否有注入
			if(SqlScriptType.checkSqlInject(jobScript.getSqlScript())) {
				throw new ServiceException("["+jobScript.getSqlScriptName()+"]有SQL注入风险，请检查SQL语句。");
			}
			jobScript.setCreateTime(LocalDateTime.now());
		}
		sqlJobScriptService.saveBatch(sqlScriptList);
		//开始操作权限组
		if(jobId != null && jobId > 0 && CollectionUtil.isNotEmpty(dataPermIdList)) {
			List<SqlJobPermission> jobPermList = new ArrayList<>();
			dataPermIdList.forEach(item -> {
				SqlJobPermission jobPermission = new SqlJobPermission();
				jobPermission.setCreateBy(AuthUtil.getNickName());
				jobPermission.setCreateTime(new Date());
				jobPermission.setCreateUserId(AuthUtil.getUserId());
				jobPermission.setDataPermId(item);
				jobPermission.setJobId(jobId);
				jobPermList.add(jobPermission);
			});
			sqlJobPermissionService.saveBatch(jobPermList);
		}
		return jobId;
	}
	
	final ExecutorService fixedThreadPool = Executors.newFixedThreadPool(10, new NamedThreadFactory("SQL-JOB", false));

	/**
	 * 允许任务
	 * @param jobId
	 * @param runType
	 */
	public void jobRun(Integer jobId, InstanceRunType runType) {
		SqlJob sqlJob = getById(jobId);
		List<SqlJobScript> jobScriptList = sqlJobScriptService.listByJobId(jobId);
		Assert.notNull(sqlJob, "SQL任务为空");
		Assert.notEmpty(jobScriptList, "SQL任务的脚本数据为空");
		List<SqlJobInstance> jobInstanceList = new ArrayList<>();
		for(SqlJobScript jobScript : jobScriptList) {
			SqlJobInstance jobInstance = new SqlJobInstance();
			jobInstance.setJobId(jobId);
			jobInstance.setJobScriptId(jobScript.getId());
			jobInstance.setJobScriptSortNum(jobScript.getSqlSortNum());
			jobInstance.setInstanceStatus(InstanceStatus.WAITING);
			jobInstance.setInstanceRunType(runType);
			jobInstance.setCreateTime(LocalDateTime.now());
			jobInstance.setTenantId(sqlJob.getTenantId());
//			jobInstance.setStartTime(new Date());
//			jobInstance.setEndTime(new Date());
			jobInstanceList.add(jobInstance);
		}
		sqlJobInstanceService.saveBatch(jobInstanceList);
		ReportDatasource datasource = reportDatasourceService.getById(sqlJob.getDsId());
		if(datasource == null) {
			throw new ServiceException("数据源不存在");
		}
		//异步执行任务
		fixedThreadPool.execute(new SqlJobThread(datasource, jobInstanceList, sqlJobInstanceService, sqlJobScriptService));
	}
}