package com.bocloud.ims.repository.resource;

import com.bocloud.cmp.entity.enums.ResourceCategory;
import com.bocloud.common.model.Pager;
import com.bocloud.common.model.Param;
import com.bocloud.common.model.SimpleBean;
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.FloatingIp;
import com.bocloud.ims.entity.resource.Volume;
import com.google.common.collect.Lists;
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;

/**
 * 浮动IP Repository接口实现
 *
 * @author weiwei
 */
@Repository("floatingIpRepository")
public class FloatingIpRepository extends BasicGenericDao<FloatingIp, Long> {

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

	public List<FloatingIp> list(int page, int rows, List<Param> params, Map<String, String> sorter) throws Exception {
		String sql = "select a.*,b.name network_name,v.name vendor_name,v.type vendor_type ,p.visibility project_visibility,t.name tenant_name, reg.`name` region_name,s.`name` server_name from floating_ip a "
				+ " left join region reg on a.region_id = reg.region_id and a.vendor_id = reg.vendor_id "
				+ " left join network b on a.network_id = b.id left join cloud_vendor v on a.vendor_id = v.id "
				+ " left join tenant t on a.tenant_id = t.id " + " left join project p on p.id = a.project_id "
				+ " left join cloud_server s on a.instance_id = s.instance_id and a.vendor_id = s.vendor_id and s.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(FloatingIp.class, sql, paramMap);
	}

	public List<SimpleBean> list(List<Param> params, Map<String, String> sorter) throws Exception {
		String sql = "select a.id,a.floating_ip_address,a.floating_ip_uuid,t.name tenant_name from floating_ip a left join tenant t on a.tenant_id = t.id where a.is_deleted = 0";
		sql = QueryBuilder.build(sql, new Pager(1, Integer.MAX_VALUE, params, sorter), "a");
		Map<String, Object> paramMap = QueryBuilder.getParam(params);
		List<FloatingIp> ips = this.list(FloatingIp.class, sql, paramMap);
		List<SimpleBean> beans = Lists.newArrayList();
		for (FloatingIp ip : ips) {
			beans.add(new SimpleBean(ip.getId(), ip.getFloatingIpAddress(), ip.getFloatingIpUuid()));
		}
		return beans;
	}

	public List<FloatingIp> listByVid(Long vendorId) throws Exception {
		String sql = "select a.*,t.name tenant_name from floating_ip 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(FloatingIp.class, sql, params);
	}

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

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

	}

	public boolean removeByVid(Long vendorId, Long userId) throws Exception {
		String sql = "update floating_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(Common.MENDER_ID, userId);
		params.put("gmtModify", new Date());
		return this.execute(sql, params) > 0;
	}

	public FloatingIp query(Long id) throws Exception {
		String sql = "select a.*,b.name network_name,t.name tenant_name, p.visibility project_visibility,p.name project_name,v.name vendor_name,u.name target_name from floating_ip a "
				+ " left join network b on a.network_id = b.id left " + " join tenant t on a.tenant_id = t.id "
				+ " left join project p on p.id = a.project_id "
				+ " left join cloud_vendor v on v.id = a.vendor_id left join object_relation o on o.res_id = a.id and o.category = '"
				+ ResourceCategory.FloatingIp.name()
				+ "' left join user u on u.id = o.target   where a.is_deleted = 0 and a.id = :id";
		Map<String, Object> params = MapTools.simpleMap(Common.ID, id);
		List<FloatingIp> list = this.list(FloatingIp.class, sql, params);
		if (list.isEmpty()) {
			return null;
		}
		return list.get(0);
	}

	public FloatingIp getByPortId(Long portId) throws Exception {
		String sql = "select a.*,t.name tenant_name from floating_ip a left join tenant t on a.tenant_id = t.id where a.is_deleted = 0 and a.port_id = :portId";
		Map<String, Object> params = MapTools.simpleMap("portId", portId);
		List<FloatingIp> list = this.list(FloatingIp.class, sql, params);
		if (list.isEmpty()) {
			return null;
		}
		return list.get(0);
	}

	public List<FloatingIp> listByRouterId(Long routerId) throws Exception {
		String sql = "select a.*,t.name tenant_name from floating_ip a left join tenant t on a.tenant_id = t.id where a.is_deleted = 0 and a.router_id = :routerId";
		Map<String, Object> params = MapTools.simpleMap("routerId", routerId);
		return this.list(FloatingIp.class, sql, params);
	}

	public FloatingIp listByServerId(String serverId) throws Exception {
		String sql = "select a.*,t.name tenant_name from floating_ip a left join port b on a.port_id = b.id left join tenant t on a.tenant_id = t.id where a.is_deleted = 0 and b.is_deleted = 0 and b.device_uuid = :serverId";
		Map<String, Object> params = MapTools.simpleMap("serverId", serverId);
		List<FloatingIp> list = this.list(FloatingIp.class, sql, params);
		if (list.isEmpty()) {
			return null;
		}
		return list.get(0);
	}

	public FloatingIp getByServerId(Long vmId) throws Exception {
		String sql = "select a.*,t.name tenant_name from floating_ip a left join port b on a.port_id = b.id left join tenant t on a.tenant_id = t.id left join cloud_server s on s.instance_id =  b.device_uuid  where a.is_deleted = 0 and b.is_deleted = 0 and s.id = :vmId limit 1";
		Map<String, Object> params = MapTools.simpleMap("vmId", vmId);
		List<FloatingIp> list = this.list(FloatingIp.class, sql, params);
		return ListTool.isEmpty(list) ? null : list.get(0);
	}

	/**
	 * 查询虚拟机绑定的浮动ip
	 *
	 * @param vmId
	 * @return
	 * @throws Exception
	 */

	public FloatingIp listByServerId(Long vmId) throws Exception {
		String sql = "select a.*,t.name tenant_name from floating_ip a left join port b on a.port_id = b.id left join tenant t on a.tenant_id = t.id left join cloud_server s on s.instance_id =  b.device_uuid  where a.is_deleted = 0 and b.is_deleted = 0 and s.id = :vmId";
		Map<String, Object> params = MapTools.simpleMap("vmId", vmId);
		List<FloatingIp> list = this.list(FloatingIp.class, sql, params);
		if (list.isEmpty()) {
			return null;
		}
		return list.get(0);
	}

	public int count(List<Param> params, Long tenantId, List<Long> projectIds) throws Exception {
		String sql = "select count(a.id) from floating_ip a left join cloud_vendor v on a.vendor_id = v.id left join tenant t on a.tenant_id = t.id,object_relation b where a.is_deleted = 0 and a.id = b.res_id and b.category = 'FloatingIp' ";
		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<FloatingIp> list(int page, int rows, List<Param> params, Map<String, String> sorter, Long tenantId,
			List<Long> projectIds) throws Exception {
		String sql = "select a.*,v.name vendor_name,v.type vendor_type,t.name tenant_name,b.expired_time, project.visibility project_visibility from floating_ip a "
				+ " left join project project on a.project_id = project.id "
				+ " left join cloud_vendor v on a.vendor_id = v.id "
				+ " left join tenant t on a.tenant_id = t.id,object_relation b where a.is_deleted = 0 and a.id = b.res_id and b.category = 'FloatingIp' ";
		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(FloatingIp.class, sql, paramMap);
	}

	public boolean inUseByTenants(Long vendorId) throws Exception {
		String sql = "select count(1) from floating_ip a where a.is_deleted = 0 and a.tenant_id is not null and a.tenant_id != 0 and a.vendor_id = :vendorId";
		Map<String, Object> params = MapTools.simpleMap(Common.VENDORID, vendorId);
		return this.countQuery(sql, params).intValue() > 0;
	}

	public List<FloatingIp> listByTenantId(Long tenantId) throws Exception {
		String sql = "select a.*,t.name tenant_name from floating_ip a left join tenant t on a.tenant_id = t.id,object_relation b,user c where a.is_deleted = 0 and a.id = b.res_id and b.target = c.id and b.category = 'FloatingIp' and c.tenant_id ="
				+ tenantId;
		return this.list(FloatingIp.class, sql);
	}

	public boolean tenantCheck(Long tenantId, Long projectId, Long creatorId) throws Exception {
		String sql = "select count(1) from floating_ip 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 List<FloatingIp> listByPortId(Long portId) throws Exception {
		String sql = "select a.* from floating_ip a where a.is_deleted = 0 and a.port_id = :portId";
		Map<String, Object> paramMap = MapTools.simpleMap("portId", portId);
		return this.list(FloatingIp.class, sql, paramMap);
	}

	public List<FloatingIp> listByTarget(Long tenantId, Long projectId, Long target) throws Exception {
		String sql = "select a.*,t.name tenant_name from floating_ip a left join tenant t on a.tenant_id = t.id,object_relation b,user c where a.is_deleted = 0 and a.id = b.res_id and b.target = c.id and b.category = 'FloatingIp' ";
		if (null != target && 0L != target) {
			sql += " and b.target = " + target;
		}
		if (null != tenantId && 0L != tenantId) {
			sql += " and a.tenant_id = " + tenantId;
		}
		if (null != projectId && 0L != projectId) {
			sql += " and a.project_id = " + projectId;
		}
		return this.list(FloatingIp.class, sql);
	}

	public Integer count(Long vendorId, Long tenantId, Long projectId) throws Exception {
		String sql = "select count(1) from floating_ip where is_deleted = 0 ";
		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);
		}
		if (null != projectId && 0L != projectId) {
			sql += " and project_id = :projectId ";
			paramMap.put("projectId", projectId);
		}
		Long count = this.countQuery(sql, paramMap);
		if (null != count) {
			return count.intValue();
		}
		return 0;
	}

	public List<FloatingIp> listByVid(Long vendorId, String regionId) throws Exception {
		String sql = "select a.*,t.name tenant_name from floating_ip 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(FloatingIp.class, sql, params);
	}

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

	public FloatingIp listByInstanceId(String instanceId, Long vendorId) throws Exception {
		String sql = " select * from floating_ip f where f.instance_id = :instanceId and f.is_deleted = 0 and f.`status` = 'BIND' and f.vendor_id = :vendorId ";
		Map<String, Object> params = MapTools.simpleMap(Common.VENDORID, vendorId);
		params.put("instanceId", instanceId);
		List<FloatingIp> list = this.list(FloatingIp.class, sql, params);
		if (list.isEmpty()) {
			return null;
		}
		return list.get(0);
	}

	public FloatingIp getByNetworkCardUuid(String networkcardUuid, Long vendorId) throws Exception {
		String sql = " select * from floating_ip f where f.networkcard_uuid = :networkcardUuid and f.is_deleted = 0 and f.`status` = 'BIND_ENI' and f.vendor_id = :vendorId ";
		Map<String, Object> params = MapTools.simpleMap(Common.VENDORID, vendorId);
		params.put("networkcardUuid", networkcardUuid);
		List<FloatingIp> list = this.list(FloatingIp.class, sql, params);
		if (list.isEmpty()) {
			return null;
		}
		return list.get(0);
	}

	public FloatingIp getByFixedIpAddress(String fixedIpAddress, Long vendorId) throws Exception {
		String sql = " select * from floating_ip f where f.fixed_ip_address = :fixedIpAddress and f.is_deleted = 0 and f.`status` = 'BIND' and f.vendor_id = :vendorId ";
		Map<String, Object> params = MapTools.simpleMap(Common.VENDORID, vendorId);
		params.put("fixedIpAddress", fixedIpAddress);
		List<FloatingIp> list = this.list(FloatingIp.class, sql, params);
		if (list.isEmpty()) {
			return null;
		}
		return list.get(0);
	}

	public FloatingIp getByInstanceId(String instanceId) throws Exception {
		String sql = "select a.*,t.name tenant_name from floating_ip a left join tenant t on a.tenant_id = t.id where a.is_deleted = 0 AND a.instance_id = :instanceId";
		Map<String, Object> params = MapTools.simpleMap("instanceId", instanceId);
		List<FloatingIp> list = this.list(FloatingIp.class, sql, params);
		if (list.isEmpty()) {
			return null;
		}
		return list.get(0);
	}

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

	public FloatingIp getByFloatingIpUuid(String floatingIpUuid, Long vendorId, String regionId) {
		String sql = "select a.* from floating_ip a where a.is_deleted = 0 and a.floating_ip_uuid = :floatingIpUuid and a.vendor_id = :vendorId and a.region_id = :regionId ";
		Map<String, Object> paramMap = MapTools.simpleMap("floatingIpUuid", floatingIpUuid);
		paramMap.put("vendorId", vendorId);
		paramMap.put("regionId", regionId);
		List<FloatingIp> list = this.list(FloatingIp.class, sql, paramMap);
		if (list.isEmpty()) {
			return null;
		}
		return list.get(0);
	}
	
    public FloatingIp getByFloatingIp(String publicIp, Long vendorId, String regionId) {
        String sql =
            "select a.* from floating_ip a where a.is_deleted = 0 and a.floating_ip_address = :publicIp and a.vendor_id = :vendorId and a.region_id = :regionId ";
        Map<String, Object> paramMap = MapTools.simpleMap("publicIp", publicIp);
        paramMap.put("vendorId", vendorId);
        paramMap.put("regionId", regionId);
        List<FloatingIp> list = this.list(FloatingIp.class, sql, paramMap);
        if (list.isEmpty()) {
            return null;
        }
        return list.get(0);
    }

	public int count(Long userId, Long tenantId, List<Long> projectIds, List<Param> params, String vendorType) {
		String subquerySql = "select a.* from floating_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<FloatingIp> 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.*,b.name network_name,v.name vendor_name,v.type vendor_type, p.visibility project_visibility,t.name tenant_name, reg.`name` region_name,s.`name` server_name from floating_ip a "
				+ " left join region reg on a.region_id = reg.region_id and a.vendor_id = reg.vendor_id "
				+ " left join network b on a.network_id = b.id left join cloud_vendor v on a.vendor_id = v.id "
				+ " left join tenant t on a.tenant_id = t.id " + " left join project p on p.id = a.project_id "
				+ " left join cloud_server s on a.instance_id = s.instance_id and a.vendor_id = s.vendor_id and s.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(FloatingIp.class, sql, paramMap);
	}
}
