package com.bocloud.ims.repository.resource;

import com.bocloud.common.model.Pager;
import com.bocloud.common.model.Param;
import com.bocloud.common.utils.Common;
import com.bocloud.common.utils.ListTool;
import com.bocloud.common.utils.MapTools;
import com.bocloud.database.core.intf.impl.BasicGenericDao;
import com.bocloud.database.utils.QueryBuilder;
import com.bocloud.ims.entity.resource.Snapshot;
import com.bocloud.ims.entity.resource.Volume;
import com.google.common.collect.Maps;
import org.apache.commons.lang3.StringUtils;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.stereotype.Repository;

import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 快照数据访问
 *
 * @author wangyu
 * @version 1.0
 * @since 2020年5月15日
 */
@Repository("snapshotRepository")
public class SnapshotRepository extends BasicGenericDao<Snapshot, Long> {

	public SnapshotRepository(JdbcTemplate jdbcTemplate, NamedParameterJdbcTemplate npJdbcTemplate) {
		super(jdbcTemplate, npJdbcTemplate);
	}

	public List<Snapshot> list(Long parentId, Long vendorId, Long serverId) throws Exception {
		Map<String, Object> params = new HashMap<>(2);
		String sql;
		if (null == parentId || parentId == 0) {
			sql = "select s.*,c.name vm_name,t.name tenant_name from snapshot s "
					+ " left join cloud_server c on s.vm_id = c.id " + " left join tenant t on s.tenant_id = t.id "
					+ " where s.is_deleted = 0 and (s.parent_id = 0 or s.parent_id is null)";
		} else {
			sql = "select s.*,c.name vm_name,t.name tenant_name from snapshot s "
					+ " left join cloud_server c on s.vm_id = c.id " + " left join tenant t on s.tenant_id = t.id "
					+ " where s.is_deleted = 0 and s.parent_id = :parentId";
			params.put("parentId", parentId);
		}
		if (null != vendorId && 0L != vendorId) {
			sql += " and s.vendor_id = :vendorId";
			params.put("vendorId", vendorId);
		}
		if (null != serverId && 0L != serverId) {
			sql += " and s.vm_id = :serverId";
			params.put("serverId", serverId);
		}
		return this.list(Snapshot.class, sql, params);
	}

	public List<Snapshot> list(int page, int rows, List<Param> params, Map<String, String> sorter) {
		String sql = "select s.*,c.name vm_name,rsg.name resource_group_name,t.name tenant_name,c.id as vendor_id from snapshot s "
				+ " left join cloud_server c on s.vm_id = c.id "
				+ " left join resource_group rsg on s.resource_group_id = rsg.id "
				+ " left join tenant t on s.tenant_id = t.id " + " where s.is_deleted = 0";
		sql = QueryBuilder.buildRaw(sql, new Pager(page, rows, params, sorter), "s");
		return this.list(Snapshot.class, sql);
	}

	public int count(List<Param> params) {
		String sql = "select count(1) from snapshot s where s.is_deleted = 0";
		sql = QueryBuilder.buildRaw(sql, params, null, "s");
		Map<String, Object> param = QueryBuilder.getParam(params);
		return this.countQuery(sql, param).intValue();
	}

	public boolean remove(Long id, Long userId) {
		String sql = "update snapshot set is_deleted = 1, gmt_modify = :gmtModify, mender_id = :menderId where id = :id";
		Map<String, Object> params = MapTools.simpleMap(Common.ID, id);
		params.put("gmtModify", new Date());
		params.put("menderId", userId);
		return this.execute(sql, params) > 0;
	}

	public List<Snapshot> listByVid(Long vendorId) {
		String sql = "select a.*,t.name tenant_name from snapshot a left join tenant t on a.tenant_id = t.id where a.is_deleted = 0 and a.vendor_id = :vendorId";
		Map<String, Object> params = MapTools.simpleMap(Common.VENDORID, vendorId);
		return this.list(Snapshot.class, sql, params);
	}

	public List<Snapshot> listByVMid(Long vmId) {
		String sql = "select a.*,t.name tenant_name from snapshot a left join tenant t on a.tenant_id = t.id where a.is_deleted = 0 and a.vm_id = :vmId";
		Map<String, Object> params = MapTools.simpleMap("vmId", vmId);
		return this.list(Snapshot.class, sql, params);
	}

	public int count(Long vmId) {
		String sql = "select count(id) from snapshot s where s.is_deleted = 0 and s.vm_id = :vmId";
		Map<String, Object> param = MapTools.simpleMap("vmId", vmId);
		return this.countQuery(sql, param).intValue();
	}

	public Snapshot getBySnapId(String snapId) {
		String sql = "select a.*,t.name tenant_name from snapshot a left join tenant t on a.tenant_id = t.id where a.is_deleted = 0 and a.snapshot_id = :snapId";
		Map<String, Object> param = MapTools.simpleMap("snapId", snapId);
		List<Snapshot> list = this.list(Snapshot.class, sql, param);
		Snapshot snap = new Snapshot();
		if (!list.isEmpty()) {
			snap = list.get(0);
		}
		return snap;
	}

	public Snapshot getBySnapUuid(String snapUuid) {
		String sql = "select a.*,t.name tenant_name from snapshot a left join tenant t on a.tenant_id = t.id where a.is_deleted = 0 and a.snapshot_uuid = :snapUuid";
		Map<String, Object> param = MapTools.simpleMap("snapUuid", snapUuid);
		List<Snapshot> list = this.list(Snapshot.class, sql, param);
		Snapshot snap = new Snapshot();
		if (!list.isEmpty()) {
			snap = list.get(0);
		}
		return snap;
	}

	public Snapshot getBySnapUuid(String snapUuid, Long vendorId) {
		String sql = "select a.* from snapshot a  where a.is_deleted = 0 and a.snapshot_uuid = :snapUuid and a.vendor_id = :vendorId ";
		Map<String, Object> param = MapTools.simpleMap("snapUuid", snapUuid);
		param.put("vendorId", vendorId);
		List<Snapshot> list = this.list(Snapshot.class, sql, param);
		if (!list.isEmpty()) {
			return list.get(0);
		}
		return null;
	}

	public List<Snapshot> listBySnapGroupId(Long snapGroupId) {
		String sql = "select a.*,t.name tenant_name from snapshot a left join tenant t on a.tenant_id = t.id where a.is_deleted = 0 and a.snapshot_group_id = :snapGroupId";
		Map<String, Object> param = MapTools.simpleMap("snapGroupId", snapGroupId);
		return this.list(Snapshot.class, sql, param);
	}

	public Snapshot query(Long id) {
		String sql = "select a.*,t.name tenant_name,rsg.name resource_group_name from snapshot a "
				+ " left join tenant t on a.tenant_id = t.id "
				+ " left join object_relation o on o.res_id = a.id and o.category = 'Snapshot' "
				+ " left join resource_group rsg on a.resource_group_id = rsg.id "
				+ " where a.is_deleted = 0 and a.id = :id";
		Map<String, Object> params = MapTools.simpleMap(Common.ID, id);
		List<Snapshot> list = this.list(Snapshot.class, sql, params);
		if (list.isEmpty()) {
			return null;
		}
		return list.get(0);
	}

	public boolean removeByVid(Long vendorId, Long userId) {
		String sql = "update snapshot set is_deleted = 1, gmt_modify = :gmtModify, mender_id = :menderId where vendor_id = :vendorId";
		Map<String, Object> params = MapTools.simpleMap(Common.VENDORID, vendorId);
		params.put("gmtModify", new Date());
		params.put("menderId", userId);
		return this.execute(sql, params) > 0;
	}

	public List<Snapshot> listByVolumeId(String volumeUuid, Long vendorId) {
		String sql = "select a.*,t.name tenant_name from snapshot a left join tenant t on a.tenant_id = t.id where a.is_deleted = 0 and a.volume_uuid = :volumeUuid and a.vendor_id = :vendorId ";
		Map<String, Object> params = MapTools.simpleMap("volumeUuid", volumeUuid);
		params.put("vendorId", vendorId);
		return this.list(Snapshot.class, sql, params);
	}

	public boolean removeByVm(Long vmId, Long userId) {
		String sql = "update snapshot set is_deleted = 1, gmt_modify = :gmtModify, mender_id = :menderId where vm_id = :vmId";
		Map<String, Object> params = MapTools.simpleMap("vmId", vmId);
		params.put("gmtModify", new Date());
		params.put("menderId", userId);
		return this.execute(sql, params) > 0;
	}

	public boolean removeByVolumeId(Long volumeId, Long userId) {
		String sql = "update snapshot set is_deleted = 1, gmt_modify = :gmtModify, mender_id = :menderId where volume_id = :volumeId";
		Map<String, Object> params = MapTools.simpleMap("volumeId", volumeId);
		params.put("gmtModify", new Date());
		params.put("menderId", userId);
		return this.execute(sql, params) > 0;
	}

	public boolean removeBySnapshotGroupId(Long snapshotGroupId, Long userId) {
		String sql = "update snapshot set is_deleted = 1, gmt_modify = :gmtModify, mender_id = :menderId where snapshot_group_id = :snapshotGroupId";
		Map<String, Object> params = MapTools.simpleMap("snapshotGroupId", snapshotGroupId);
		params.put("gmtModify", new Date());
		params.put("menderId", userId);
		return this.execute(sql, params) > 0;
	}

	public int count(List<Param> params, String type) {
		String sql = "select count(1) from snapshot s,cloud_vendor c where s.is_deleted = 0 and s.vendor_id = c.id";
		if (!StringUtils.isEmpty(type)) {
			sql += " and c.type = '" + type + "'";
		}
		sql = QueryBuilder.buildRaw(sql, params, null, "s");
		Map<String, Object> param = QueryBuilder.getParam(params);
		return this.countQuery(sql, param).intValue();
	}

	public List<Snapshot> listByVolumeId(Long volumeId) {
		String sql = "select a.*,t.name tenant_name from snapshot a left join tenant t on a.tenant_id = t.id where a.is_deleted = 0 and a.volume_id = :volumeId";
		Map<String, Object> params = MapTools.simpleMap("volumeId", volumeId);
		return this.list(Snapshot.class, sql, params);
	}

	public int count(List<Param> params, Long tenantId, List<Long> projectIds) {
		String sql = "select count(a.id) from snapshot a " + " left join tenant t on a.tenant_id = t.id "
				+ " left join cloud_vendor v on a.vendor_id = v.id "
				+ " left join resource_group rsg on a.resource_group_id = rsg.id " + " where a.is_deleted = 0 ";
		Map<String, Object> paramMap = QueryBuilder.getParam(params);
		if (null != tenantId && 0L != tenantId) {
			sql += " and a.tenant_id = :tenantId";
			paramMap.put("tenantId", tenantId);
		}
		if (!ListTool.isEmpty(projectIds)) {
			sql += " and a.project_id in (:projectIds) ";
			paramMap.put("projectIds", projectIds);
		}
		sql = QueryBuilder.buildRaw(sql, params, null, "a");
		return this.countQuery(sql, paramMap).intValue();
	}

	public List<Snapshot> list(int page, int rows, List<Param> params, Map<String, String> sorter, Long tenantId,
			List<Long> projectIds) {
		String sql = "select a.*,v.name vendor_name,rsg.name resource_group_name,v.type vendor_type,t.name tenant_name from snapshot a "
				+ " left join tenant t on a.tenant_id = t.id "
				+ " left join resource_group rsg on a.resource_group_id = rsg.id "
				+ " left join cloud_vendor v on a.vendor_id = v.id " + " where a.is_deleted = 0 ";
		Map<String, Object> paramMap = QueryBuilder.getParam(params);
		if (null != tenantId && 0L != tenantId) {
			sql += " and a.tenant_id = :tenantId";
			paramMap.put("tenantId", tenantId);
		}
		if (!ListTool.isEmpty(projectIds)) {
			sql += " and a.project_id in (:projectIds) ";
			paramMap.put("projectIds", projectIds);
		}
		sql = QueryBuilder.buildRaw(sql, new Pager(page, rows, params, sorter), "a");
		return this.list(Snapshot.class, sql, paramMap);
	}

	public boolean tenantCheck(Long tenantId, Long creatorId) {
		String sql = "select count(1) from snapshot a where a.is_deleted = 0 ";
		Map<String, Object> paramMap = Maps.newHashMap();
		if (null != tenantId && 0L != tenantId) {
			sql += " and a.tenant_id = :tenantId";
			paramMap.put("tenantId", tenantId);
		}
		if (null != creatorId && 0L != creatorId) {
			sql += " and a.creator_id = :creatorId";
			paramMap.put("creatorId", creatorId);
		}
		return this.countQuery(sql, paramMap).intValue() > 0;
	}

	public boolean hasBuilding(Long vendorId) {
		String sql = "select count(a.id) from snapshot a where a.is_deleted = 0 and a.status = 'BUILDING' and a.vendor_id = :vendorId";
		Map<String, Object> paramMap = MapTools.simpleMap(Common.VENDORID, vendorId);
		return this.countQuery(sql, paramMap) > 0;
	}

	public List<Snapshot> listBootableSnaps(Long vendorId, String tenantUuid, Long tenantId) {
		String sql = "select a.* from snapshot a left join volume b on a.volume_id = b.id where a.is_deleted = 0 and a.status = 'AVAILABLE' and b.is_deleted = 0 and b.boot_volume = 1 and a.vendor_id = :vendorId and a.tenant_id = :tenantId";
		Map<String, Object> params = MapTools.simpleMap(Common.VENDORID, vendorId);
		params.put("tenantId", tenantId);
		if (!StringUtils.isEmpty(tenantUuid)) {
			sql += " and b.tenant_uuid = :tenantUuid";
			params.put("tenantUuid", tenantUuid);
		}
		return this.list(Snapshot.class, sql, params);
	}

	public Integer sizeSum(Long vendorId, Long tenantId) {
		String sql = "select sum(size) from snapshot where is_deleted = 0 and size is not null";
		Map<String, Object> paramMap = Maps.newHashMap();
		if (null != vendorId && 0L != vendorId) {
			sql += " and vendor_id = :vendorId ";
			paramMap.put(Common.VENDORID, vendorId);
		}
		if (null != tenantId && 0L != tenantId) {
			sql += " and tenant_id = :tenantId ";
			paramMap.put("tenantId", tenantId);
		}
		Long count = this.countQuery(sql, paramMap);
		if (null != count) {
			return count.intValue();
		}
		return 0;
	}

	public Snapshot getByName(Long vendorId, String name) {
		String sql = "select a.* from snapshot a where a.is_deleted = 0 and a.vendor_id = :vendorId and a.name = :name";
		Map<String, Object> paramMap = MapTools.simpleMap(Common.VENDORID, vendorId);
		paramMap.put("name", name);
		List<Snapshot> list = this.list(Snapshot.class, sql, paramMap);
		if (list.isEmpty()) {
			return null;
		}
		return list.get(0);
	}

	public List<Snapshot> listByVid(Long vendorId, String regionId) {
		String sql = "select a.*,t.name tenant_name from snapshot a left join tenant t on a.tenant_id = t.id where a.is_deleted = 0 and a.vendor_id = :vendorId"
				+ " and a.region_id = :regionId ";
		Map<String, Object> params = MapTools.simpleMap(Common.VENDORID, vendorId);
		params.put(Common.REGIONID, regionId);
		return this.list(Snapshot.class, sql, params);
	}

	public boolean removeByVid(Long vendorId, String regionId, Long userId) {
		String sql = "update snapshot set is_deleted = 1, gmt_modify = :gmtModify, mender_id = :menderId where vendor_id = :vendorId"
				+ " and region_id = :regionId ";
		Map<String, Object> params = MapTools.simpleMap(Common.VENDORID, vendorId);
		params.put(Common.REGIONID, regionId);
		params.put("gmtModify", new Date());
		params.put("menderId", userId);
		return this.execute(sql, params) > 0;
	}

	public boolean removeBySnapUuid(String snapUuid, Long vendorId, Long userId) {
		String sql = "update snapshot set is_deleted = 1, gmt_modify = :gmtModify, mender_id = :menderId where vendor_id = :vendorId"
				+ " and snapshot_uuid = :snapUuid ";
		Map<String, Object> params = MapTools.simpleMap(Common.VENDORID, vendorId);
		params.put("snapUuid", snapUuid);
		params.put("gmtModify", new Date());
		params.put("menderId", userId);
		return this.execute(sql, params) > 0;
	}

	public boolean tenantCheck(Long tenantId, Long projectId, Long creatorId) throws Exception {
		String sql = "select count(1) from snapshot a where a.is_deleted = 0 ";
		Map<String, Object> paramMap = Maps.newHashMap();
		if (null != tenantId && 0L != tenantId) {
			sql += " and a.tenant_id = :tenantId";
			paramMap.put("tenantId", tenantId);
		}
		if (null != projectId && 0L != projectId) {
			sql += " and a.project_id = :projectId";
			paramMap.put("projectId", projectId);
		}
		if (null != creatorId && 0L != creatorId) {
			sql += " and a.creator_id = :creatorId";
			paramMap.put("creatorId", creatorId);
		}
		return this.countQuery(sql, paramMap).intValue() > 0;
	}

	public boolean removeByGroupName(Long vendorId, Long userId, String regionId, String resourceGroupName) {
		String sql = "update snapshot set is_deleted = 1, gmt_modify = :gmtModify, mender_id = :menderId where vendor_id = :vendorId and azure_resource_group_name = :resourceGroupName and region_id = :regionId ";
		Map<String, Object> params = MapTools.simpleMap(Common.VENDORID, vendorId);
		params.put("resourceGroupName", resourceGroupName);
		params.put("regionId", regionId);
		params.put("gmtModify", new Date());
		params.put("menderId", userId);
		return this.execute(sql, params) > 0;
	}

	public int count(Long userId, Long tenantId, List<Long> projectIds, List<Param> params, String vendorType) {
		String subquerySql = "select a.* from snapshot a left join cloud_vendor c on a.vendor_id = c.id   "
				+ " where a.is_deleted = 0 ";
		if (!StringUtils.isEmpty(vendorType)) {
			subquerySql += " and c.type = '" + vendorType + "'  ";
		}
		Map<String, Object> param = QueryBuilder.getParam(params);
		subquerySql = QueryBuilder.build(subquerySql, params, new HashMap<>(8), "a");
		String sql = "select distinct count(1) from (" + subquerySql + ") c  where ( 1=-1 ";
		if (null != userId && 0L != userId) {
			sql += " or c.owner_id =:userId or c.mender_id = :userId ";
			param.put("userId", userId);
		}
		if (null != tenantId && 0L != tenantId) {
			sql += " or c.tenant_id = :tenantId";
			param.put("tenantId", tenantId);
		}
		if (null != projectIds && !projectIds.isEmpty()) {
			sql += " or c.project_id in (:projectIds) ";
			param.put("projectIds", projectIds);
		}
		sql += ")";
		return this.countQuery(sql, param).intValue();
	}

	public List<Snapshot> list(int page, int rows, Long userId, Long tenantId, List<Long> projectIds,
			List<Param> params, Map<String, String> sorter, String vendorType) {
		String subquerySql = "select s.*,c.name vm_name,rsg.name resource_group_name,t.name tenant_name from snapshot s "
				+ " left join cloud_server c on s.vm_id = c.id left join cloud_vendor v on s.vendor_id = v.id "
				+ " left join resource_group rsg on s.resource_group_id = rsg.id "
				+ " left join tenant t on s.tenant_id = t.id where s.is_deleted = 0";
		if (!StringUtils.isEmpty(vendorType)) {
			subquerySql += " and v.type = '" + vendorType + "'  ";
		}
		subquerySql = QueryBuilder.build(subquerySql, params, new HashMap<>(8), "s");
		Map<String, Object> paramMap = QueryBuilder.getParam(params);
		String sql = "select distinct  d.*  from (" + subquerySql + ") d  where ( 1=-1 ";
		if (null != userId && 0L != userId) {
			sql += " or d.owner_id =:userId or d.mender_id = :userId ";
			paramMap.put("userId", userId);
		}
		if (null != tenantId && 0L != tenantId) {
			sql += " or d.tenant_id = :tenantId";
			paramMap.put("tenantId", tenantId);
		}
		if (null != projectIds && !projectIds.isEmpty()) {
			sql += " or d.project_id in (:projectIds) ";
			paramMap.put("projectIds", projectIds);
		}
		sql += " )";
		sql = QueryBuilder.buildRaw(sql, new Pager(page, rows, null, sorter), "d");
		return this.list(Snapshot.class, sql, paramMap);
	}
}
