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.MapTools;
import com.bocloud.database.core.intf.impl.BasicGenericDao;
import com.bocloud.database.utils.QueryBuilder;
import com.bocloud.ims.entity.resource.ElasticIp;
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.*;

@Repository("elasticIpRepository")
public class ElasticIpRepository extends BasicGenericDao<ElasticIp, Long> {

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

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

	public List<ElasticIp> list(int page, int rows, List<Param> params, Map<String, String> sorter) throws Exception {
		String sql = "select a.*,v.type vendor_type,r.name region_name ,g.display_name resource_group_name from elastic_ip a "
				+ " left join cloud_vendor v on a.vendor_id=v.id "
				+ " left join region r on r.vendor_id = a.vendor_id and r.region_id = a.region_id"
				+ " left join resource_group g on a.resource_group_uuid = g.resource_group_uuid and a.vendor_id = g.vendor_id and g.is_deleted = 0 "
				+ " where a.is_deleted = 0";
		sql = QueryBuilder.buildRaw(sql, new Pager(page, rows, params, sorter), "a");
		Map<String, Object> paramMap = QueryBuilder.getParam(params);
		return this.list(ElasticIp.class, sql, paramMap);
	}

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

	public ElasticIp query(Long id) throws Exception {
		String sql = "select a.* from elastic_ip a where a.is_deleted = 0 and a.id = :id";
		Map<String, Object> params = MapTools.simpleMap(Common.ID, id);
		List<ElasticIp> list = this.list(ElasticIp.class, sql, params);
		return list.isEmpty() ? null : list.get(0);
	}

	public ElasticIp query(String uuid) throws Exception {
		String sql = "select a.* from elastic_ip a where a.is_deleted = 0 and a.elastic_ip_uuid = :uuid";
		Map<String, Object> params = MapTools.simpleMap("uuid", uuid);
		List<ElasticIp> list = this.list(ElasticIp.class, sql, params);
		return list.isEmpty() ? null : list.get(0);
	}

	public ElasticIp queryByAllocationId(String associationId) throws Exception {
		String sql = "select a.* from elastic_ip a where a.is_deleted = 0 and a.association_id = :associationId";
		Map<String, Object> params = MapTools.simpleMap("associationId", associationId);
		List<ElasticIp> list = this.list(ElasticIp.class, sql, params);
		return list.isEmpty() ? null : list.get(0);
	}

	public ElasticIp queryByPublicIp(String publicIp) throws Exception {
		String sql = "select a.* from elastic_ip a where a.is_deleted = 0 and a.public_ip = :publicIp";
		Map<String, Object> params = MapTools.simpleMap("publicIp", publicIp);
		List<ElasticIp> list = this.list(ElasticIp.class, sql, params);
		return list.isEmpty() ? null : list.get(0);
	}

	public ElasticIp queryByPublicIp(String publicIp, Long vendorId) throws Exception {
		String sql = "select a.* from elastic_ip a where a.is_deleted = 0 and a.public_ip = :publicIp and a.vendor_id = :vendorId ";
		Map<String, Object> params = MapTools.simpleMap("publicIp", publicIp);
		params.put("vendorId", vendorId);
		List<ElasticIp> list = this.list(ElasticIp.class, sql, params);
		return list.isEmpty() ? null : list.get(0);
	}

	public ElasticIp queryByAssociatedId(String associatedId, Long vendorId) throws Exception {
		String sql = "select a.* from elastic_ip a where (a.instance_id = :associatedId or a.network_interface_id = :associatedId) and a.is_deleted = 0 and a.vendor_id = :vendorId";
		Map<String, Object> params = MapTools.simpleMap("vendorId", vendorId);
		params.put("associatedId", associatedId);
		List<ElasticIp> list = this.list(ElasticIp.class, sql, params);
		return list.isEmpty() ? null : list.get(0);
	}

	public List<ElasticIp> listByIid(String instanceId) {
		String sql = "select a.* from elastic_ip a where a.is_deleted = 0 and a.instance_id =:instanceId";
		Map<String, Object> params = MapTools.simpleMap("instanceId", instanceId);
		return this.list(ElasticIp.class, sql, params);
	}

	public List<ElasticIp> listByVid(Long vendorId) throws Exception {
		String sql = "select a.*,v.name vendor_name from elastic_ip a left join cloud_vendor v on v.id = a.vendor_id where a.is_deleted = 0 and a.vendor_id = :vendorId";
		Map<String, Object> paramMap = MapTools.simpleMap(Common.VENDORID, vendorId);
		return this.list(ElasticIp.class, sql, paramMap);
	}

	public List<ElasticIp> listByVid(Long vendorId, String regionId) throws Exception {
		String sql = "select a.*,v.name vendor_name from elastic_ip a left join cloud_vendor v on v.id = a.vendor_id where a.is_deleted = 0 and a.region_id = :regionId and a.vendor_id = :vendorId";
		Map<String, Object> paramMap = MapTools.simpleMap(Common.VENDORID, vendorId);
		paramMap.put("regionId", regionId);
		return this.list(ElasticIp.class, sql, paramMap);
	}

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

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

	public List<ElasticIp> listEips(Long userId, Long tenantId, List<Long> projectIds, String regionId, Long vendorId) {
		String subquerySql = "select a.* from elastic_ip a where a.is_deleted = 0 and a.vendor_id = :vendorId and a.region_id = :regionId and a.instance_id is null";

		Map<String, Object> params = MapTools.simpleMap("vendorId", vendorId);
		params.put("regionId", regionId);
		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 ";
			params.put("userId", userId);
		}
		if (null != tenantId && 0L != tenantId) {
			sql += " or d.tenant_id = :tenantId";
			params.put("tenantId", tenantId);
		}
		if (null != projectIds && !projectIds.isEmpty()) {
			sql += " or d.project_id in (:projectIds) ";
			params.put("projectIds", projectIds);
		}
		sql += " )";
		return this.list(ElasticIp.class, sql, params);
	}

	public List<ElasticIp> listAvailableEipsForNatGateway(Long vendorId, String ip, String regionId) {
		String sql = "select a.* from elastic_ip a where a.is_deleted = 0 and a.status = 'DOWN' and a.vendor_id = :vendorId and a.region_id = :regionId "
				+ "and a.id not in (select elastic_ip_id from nat_gateway_dnat_rule where protocol = 'ANY' and is_deleted = 0) ";
		Map<String, Object> params = MapTools.simpleMap("vendorId", vendorId);
		params.put("regionId", regionId);

		if (ip != null) {
			sql = sql + " and a.public_ip like :ip";
			params.put("ip", "%" + ip + "%");
		}
		return this.list(ElasticIp.class, sql, params);
	}

	public List<ElasticIp> listAvailableEipsForDnat(Long vendorId) {
		String sql = "select a.* from elastic_ip a where a.is_deleted = 0 and a.status = 'DOWN' and a.vendor_id = :vendorId and "
				+ "a.id not in (select elastic_ip_id from nat_gateway_snat_rule_relation) ";
		Map<String, Object> params = MapTools.simpleMap("vendorId", vendorId);
		return this.list(ElasticIp.class, sql, params);
	}

	public List<ElasticIp> listByIid(String instanceId, Long vendorId) {
		String sql = "select a.* from elastic_ip a where a.is_deleted = 0 and a.instance_id =:instanceId and a.vendor_id =:vendorId";
		Map<String, Object> params = MapTools.simpleMap("instanceId", instanceId);
		params.put(Common.VENDORID, vendorId);
		return this.list(ElasticIp.class, sql, params);
	}

	public List<ElasticIp> listByIid(String instanceId, Long vendorId, String instanceType) {
		String sql = "select a.* from elastic_ip a where a.is_deleted = 0 and a.instance_id =:instanceId and instance_type = :instanceType and a.vendor_id =:vendorId ";
		Map<String, Object> params = MapTools.simpleMap("instanceId", instanceId);
		params.put(Common.VENDORID, vendorId);
		params.put("instanceType", instanceType);
		return this.list(ElasticIp.class, sql, params);
	}

	public Boolean removeByInstance(String instanceId, String instanceType, Long userId, String regionId, Long vendorId)
			throws Exception {
		String sql = "update elastic_ip set  instance_id = null, instance_type = null ,gmt_modify = :gmtModify,mender_id = :menderId "
				+ " where is_deleted = 0 and region_id = :regionId and vendor_id = :vendorId and instance_id =:instanceId and instance_type = :instanceType ";
		Map<String, Object> params = MapTools.simpleMap(Common.VENDORID, vendorId);
		params.put("regionId", regionId);
		params.put("instanceId", instanceId);
		params.put("instanceType", instanceType);
		params.put("menderId", userId);
		params.put("gmtModify", new Date());
		return this.execute(sql, params) > 0;
	}

	public List<ElasticIp> listBindNatGatewaySnat(Long vendorId, Long snatRuleId) {
		String sql = "select a.* from elastic_ip a where a.is_deleted = 0 and a.vendor_id = :vendorId and "
				+ "a.id in (select n.elastic_ip_id from nat_gateway_snat_rule_relation n where n.snat_rule_id = :snatRuleId) ";
		Map<String, Object> params = MapTools.simpleMap("vendorId", vendorId);
		params.put("snatRuleId", snatRuleId);
		return this.list(ElasticIp.class, sql, params);
	}

	public List<ElasticIp> listAvailableEipsForDnat(Long vendorId, String regionId) {
		String sql = "select a.* from elastic_ip a where a.is_deleted = 0 and a.status = 'DOWN' and a.vendor_id = :vendorId and a.region_id = :regionId and "
				+ "a.id not in (select elastic_ip_id from nat_gateway_snat_rule_relation) ";
		Map<String, Object> params = MapTools.simpleMap("vendorId", vendorId);
		params.put("regionId", regionId);
		return this.list(ElasticIp.class, sql, params);
	}

	public List<ElasticIp> listBindNatGatewayDnat(Long vendorId, Long dnatRuleId) {
		String sql = "select a.* from elastic_ip a where a.is_deleted = 0 and a.vendor_id = :vendorId and "
				+ " a.id in (select n.elastic_ip_id from nat_gateway_dnat_rule n where n.is_deleted = 0 and n.id = :dnatRuleId) ";
		Map<String, Object> params = MapTools.simpleMap("vendorId", vendorId);
		params.put("dnatRuleId", dnatRuleId);
		return this.list(ElasticIp.class, sql, params);
	}

	public int count(Long userId, Long tenantId, List<Long> projectIds, List<Param> params, String vendorType) {
		String subquerySql = "select a.* from elastic_ip 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<ElasticIp> list(int page, int rows, Long userId, Long tenantId, List<Long> projectIds,
			List<Param> params, Map<String, String> sorter, String vendorType) {
		String subquerySql = "select a.*,v.type vendor_type,r.name region_name ,g.display_name resource_group_name from elastic_ip a "
				+ " left join cloud_vendor v on a.vendor_id= v.id "
				+ " left join region r on r.vendor_id = a.vendor_id and r.region_id = a.region_id"
				+ " left join resource_group g on a.resource_group_uuid = g.resource_group_uuid and a.vendor_id = g.vendor_id and g.is_deleted = 0 "
				+ " where a.is_deleted = 0";
		if (!StringUtils.isEmpty(vendorType)) {
			subquerySql += " and v.type = '" + vendorType + "'  ";
		}
		subquerySql = QueryBuilder.build(subquerySql, params, new HashMap<>(8), "a");
		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(ElasticIp.class, sql, paramMap);
	}

	public ElasticIp getByUuid(String uuid) {
		String sql = "select a.* from elastic_ip a where a.is_deleted = 0 and a.uuid = :uuid";
		Map<String, Object> paramMap = MapTools.simpleMap("uuid", uuid);
		List<ElasticIp> list = this.list(ElasticIp.class, sql, paramMap);
		if (list.isEmpty()) {
			return null;
		}
		return list.get(0);
	}
}
