package com.bocloud.ims.repository.resource;

import com.bocloud.common.exception.BoCloudDBException;
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.IDFactory;
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.Subnet;
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.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;

/**
 * 子网Repository接口实现
 *
 * @author weiwei
 */
@Repository("subnetRepository")
public class SubnetRepository extends BasicGenericDao<Subnet, Long> {

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

    @Override
    public void save(Subnet subnet) throws BoCloudDBException {
        subnet.setGuid(IDFactory.instance().uuid());
        super.save(subnet);
    }

    public List<Subnet> list(int page, int rows, List<Param> params, Map<String, String> sorter, Long routerId) {
        String sql = null;
        if (null != routerId && 0L != routerId) {
            sql = "select a.*, r.name region_name, c.type vendor_type from subnet a "
                + " left join region r on a.vendor_id = r.vendor_id and r.region_id = a.region_id "
                + " left join cloud_vendor c on a.vendor_id = c.id "
                + " left join router_subnet b on a.id = b.subnet_id where a.is_deleted = 0 and b.router_id = "
                + routerId;
        } else {
            sql = "select a.* , z.name zone_name, r.name region_name, c.type vendor_type from subnet  a "
                + " left join region r on a.vendor_id = r.vendor_id and r.region_id = a.region_id "
                + " left join cloud_vendor c on a.vendor_id = c.id "
                + " left join zone z on a.zone = z.zone_id and z.vendor_id = a.vendor_id " + "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(Subnet.class, sql, paramMap);
    }

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

    public int count(List<Param> params, Long routerId) {
        String sql = null;
        if (null != routerId && 0L != routerId) {
            sql =
                "select count(1) from subnet a left join router_subnet b on a.id = b.subnet_id  where a.is_deleted = 0 and b.router_id = "
                    + routerId;
        } else {
            sql = "select count(1) from subnet 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) {
        String sql =
            "update subnet 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("menderId", userId);
        params.put("gmtModify", new Date());
        return this.execute(sql, params) > 0;
    }

    public boolean remove(Long id) {
        String sql = "update subnet set is_deleted = true,gmt_modify = :gmtModify where is_deleted = 0 and id = :id";
        Map<String, Object> params = MapTools.simpleMap("id", id);
        params.put("gmtModify", new Date());
        return this.execute(sql, params) > 0;
    }

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

    public List<SimpleBean> list(List<Param> params, Map<String, String> sorter) {
        String sql =
            "select a.id,a.name,a.cidr, a.subnet_uuid,b.name network_name from subnet a left join network b on a.network_id = b.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<Subnet> subnets = this.list(Subnet.class, sql, paramMap);
        List<SimpleBean> beans = new ArrayList<>();
        for (Subnet subnet : subnets) {
            beans.add(new SimpleBean(subnet.getId(), subnet.getName() + "(" + subnet.getCidr() + ")",
                subnet.getSubnetUuid()));
        }
        return beans;
    }

    public boolean removeByVid(Long vendorId, Long userId) {
        String sql =
            "update subnet 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 List<Subnet> listByVid(Long vendorId) {
        String sql = "select a.* from subnet a where a.is_deleted = 0 and a.vendor_id = :vendorId";
        Map<String, Object> params = MapTools.simpleMap(Common.VENDORID, vendorId);
        return this.list(Subnet.class, sql, params);
    }

    public List<Subnet> listByRouterId(Long routerId) {
        String sql =
            "select a.* from subnet a left join router_subnet b on a.id = b.subnet_id where a.is_deleted = 0 and b.router_id = :routerId";
        Map<String, Object> params = MapTools.simpleMap("routerId", routerId);
        return this.list(Subnet.class, sql, params);
    }

    public List<Subnet> listByRouteId(Long routeId) {
        String sql = "select a.* from subnet a  where a.is_deleted = 0 and a.route_table_id = :routeId";
        Map<String, Object> params = MapTools.simpleMap("routeId", routeId);
        return this.list(Subnet.class, sql, params);
    }

    public Subnet getBySubId(String subnetUuid) {
        String sql = "select a.* from subnet a where a.is_deleted = 0 and a.subnet_uuid = :subnetUuid";
        Map<String, Object> param = MapTools.simpleMap("subnetUuid", subnetUuid);
        List<Subnet> list = this.list(Subnet.class, sql, param);
        if (list.isEmpty()) {
            return null;
        }
        return list.get(0);
    }

    public boolean removeByNetworkId(Long networkId, Long userId) {
        String sql =
            "update subnet set is_deleted = true,gmt_modify = :gmtModify,mender_id = :menderId where is_deleted = 0 and network_id = :networkId";
        Map<String, Object> params = MapTools.simpleMap("networkId", networkId);
        params.put("menderId", userId);
        params.put("gmtModify", new Date());
        return this.execute(sql, params) > 0;
    }

    public boolean resetNetwork(Long vendorId, Long networkId) {
        String sql =
            "update subnet set network_id = null,network_uuid = null where is_deleted = 0 and vendor_id = :vendorId and network_id = :networkId";
        Map<String, Object> params = MapTools.simpleMap(Common.VENDORID, vendorId);
        params.put("networkId", networkId);
        return this.execute(sql, params) > 0;
    }

    public List<Subnet> listByNetworkUuid(String networkUuid, Long vendorId) {
        String sql =
            "select a.* from subnet a where a.is_deleted = 0 and network_uuid = :networkUuid and vendor_id = :vendorId";
        Map<String, Object> params = MapTools.simpleMap("networkUuid", networkUuid);
        params.put(Common.VENDORID, vendorId);
        return this.list(Subnet.class, sql, params);
    }

    public List<Subnet> listByNetwork(Long networkId) {
        String sql = "select a.* from subnet a where a.is_deleted = 0 and a.network_id = :networkId";
        Map<String, Object> params = MapTools.simpleMap("networkId", networkId);
        return this.list(Subnet.class, sql, params);
    }

    public List<Subnet> listByVendorId(Long vendorId, Long networkId, String tenantUuid) {
        String sql = "select a.* from subnet a where a.is_deleted = 0 and a.vendor_id = :vendorId";
        Map<String, Object> paramMap = MapTools.simpleMap(Common.VENDORID, vendorId);
        if (null != networkId && 0L != networkId) {
            sql += " and a.network_id = :networkId";
            paramMap.put("networkId", networkId);
        }
        if (!StringUtils.isEmpty(tenantUuid)) {
            sql += " and a.tenant_uuid = :tenantUuid";
            paramMap.put("tenantUuid", tenantUuid);
        }
        return this.list(Subnet.class, sql, paramMap);
    }

    public List<Subnet> listAttachByRouter(Long vendorId, Long routerId) {
        String sql =
            "select a.* from subnet a where a.is_deleted = 0 and a.id in (select b.subnet_id from router_subnet b where b.router_id = "
                + routerId + " and b.vendor_id = " + vendorId + ")";
        return this.list(Subnet.class, sql);
    }

    public List<Subnet> listDetach(Long vendorId, Long networkId, String tenantUuid) {
        String sql = "select a.* from subnet a where a.is_deleted = 0 and a.vendor_id = " + vendorId
                + " and a.id not in (select DISTINCT  b.subnet_id from router_subnet b where b.vendor_id =  " + vendorId + ")";
        if (null != networkId && 0L != networkId) {
            sql += " and a.network_id =" + networkId;
        }
        if (!StringUtils.isEmpty(tenantUuid)) {
            sql += " and a.tenant_uuid = '" + tenantUuid + "'";
        }
        return this.list(Subnet.class, sql);
    }

    public List<Subnet> listDetachByRouter(Long vendorId, Long routerId, Long networkId, String tenantUuid) {
        String sql = "select a.* from subnet a where a.is_deleted = 0 and a.vendor_id = " + vendorId
            + " and a.id not in (select b.subnet_id from router_subnet b where b.router_id = " + routerId + ")";
        if (null != networkId && 0L != networkId) {
            sql += " and a.network_id =" + networkId;
        }
        if (!StringUtils.isEmpty(tenantUuid)) {
            sql += " and a.tenant_uuid = '" + tenantUuid + "'";
        }
        return this.list(Subnet.class, sql);
    }

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

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

    public List<Subnet> listByRegion(Long vendorId, String regionId) {
        String sql =
            "select a.* from subnet a 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(Subnet.class, sql, params);
    }

    public List<Subnet> listByIds(String buildParam) {
        String sql = "select a.* from subnet a where a.is_deleted = 0 and a.id in( :ids ) ";
        Map<String, Object> params = MapTools.simpleMap("ids", buildParam);
        return this.list(Subnet.class, sql, params);
    }

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

    public List<Subnet> listByVpcId(Long vendorId, String vpcId) {
        String sql =
            "select a.* from subnet a where a.is_deleted = 0 and a.vpc_uuid = :vpcId and a.vendor_id = :vendorId";
        Map<String, Object> params = MapTools.simpleMap("vpcId", vpcId);
        params.put(Common.VENDORID, vendorId);
        return this.list(Subnet.class, sql, params);
    }

    public List<Subnet> listByVpcId(Long vendorId, Long vpcId) {
        String sql =
            "select a.* from subnet a where a.is_deleted = 0 and a.vpc_id = :vpcId and a.vendor_id = :vendorId";
        Map<String, Object> params = MapTools.simpleMap("vpcId", vpcId);
        params.put(Common.VENDORID, vendorId);
        return this.list(Subnet.class, sql, params);
    }

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

    public List<Subnet> shareSubnets(Long vendorId, Long networkId) {
        String sql =
            "select a.* from subnet a left join network b on b.id = a.network_id where a.is_deleted = 0 and b.shared = 1 and a.vendor_id = :vendorId";
        Map<String, Object> params = MapTools.simpleMap(Common.VENDORID, vendorId);
        if (null != networkId && 0L != networkId) {
            sql += " and a.network_id = :networkId";
            params.put("networkId", networkId);
        }
        return this.list(Subnet.class, sql, params);
    }

    public List<Subnet> list(int page, int rows, List<Param> params, Map<String, String> sorter) throws Exception {
        String sql = "select a.* from subnet a where a.is_deleted = 0";
        sql = QueryBuilder.build(sql, new Pager(page, rows, params, sorter), "");
        Map<String, Object> paramMap = QueryBuilder.getParam(params);
        return this.list(Subnet.class, sql, paramMap);
    }

    public Subnet query(String subnetUuid, Long vendorId) {
        String sql =
            "select a.* from subnet a where a.is_deleted = 0 and a.subnet_uuid = :subnetUuid  and a.vendor_id = :vendorId";
        Map<String, Object> param = MapTools.simpleMap("subnetUuid", subnetUuid);
        param.put(Common.VENDORID, vendorId);
        List<Subnet> list = this.list(Subnet.class, sql, param);
        if (list.isEmpty()) {
            return null;
        }
        return list.get(0);
    }

    public List<Subnet> listSubnet(Long natGatewayId, Long vpcId) {
        String sql = "select a.* from subnet a where a.is_deleted = 0 and a.vpc_id = :vpcId and "
            + "a.id not in (select subnet_id from nat_gateway_snat_rule where id = :natGatewayId  and  is_deleted = 0 )";
        Map<String, Object> params = MapTools.simpleMap("vpcId", vpcId);
        params.put("natGatewayId", natGatewayId);
        return this.list(Subnet.class, sql, params);
    }

    public List<String> getUuidByRouteTableUuid(Long vendorId, String regionId, String routeTableUuid) {
        List<String> uuids = new ArrayList<>();
        String sql =
            "select a.* from subnet a where a.is_deleted = 0   and a.region_id = :regionId and a.vendor_id = :vendorId "
                + " and route_table_id = :routeTableUuid ";
        Map<String, Object> params = MapTools.simpleMap("vendorId", vendorId);
        params.put("routeTableUuid", routeTableUuid);
        params.put("regionId", regionId);
        List<Subnet> list = this.list(Subnet.class, sql, params);
        if (null == list || list.isEmpty()) {
            return uuids;
        }
        for (Subnet subnet : list) {
            if (null != subnet && !StringUtils.isEmpty(subnet.getSubnetUuid())) {
                uuids.add(subnet.getSubnetUuid());
            }
        }
        return uuids;
    }

	public Subnet getByNeutronSubnetUuid(String neutronSubnetUuid, Long vendorId) {
		String sql = "select a.* from subnet a where a.is_deleted = 0 and a.neutron_subnet_uuid = :neutronSubnetUuid and a.vendor_id = :vendorId";
		Map<String, Object> param = MapTools.simpleMap("neutronSubnetUuid", neutronSubnetUuid);
		param.put(Common.VENDORID, vendorId);
		List<Subnet> list = this.list(Subnet.class, sql, param);
		if (list.isEmpty()) {
			return null;
		}
		return list.get(0);
	}
    
    public Subnet getBySubId(String subnetUuid, Long vendorId) {
        String sql =
            "select a.* from subnet a where a.is_deleted = 0 and a.subnet_uuid = :subnetUuid and a.vendor_id = :vendorId";
        Map<String, Object> param = MapTools.simpleMap("subnetUuid", subnetUuid);
        param.put(Common.VENDORID, vendorId);
        List<Subnet> list = this.list(Subnet.class, sql, param);
        if (list.isEmpty()) {
            return null;
        }
        return list.get(0);
    }

    public List<Subnet> list(int page, int rows, List<Param> params, Map<String, String> sorter, Long routerId,
        Boolean hasIpv6) {
        String sql = null;
        if (null != routerId && 0L != routerId) {
            sql = "select a.*, r.name region_name, c.type vendor_type from subnet a "
                + " left join region r on a.vendor_id = r.vendor_id and r.region_id = a.region_id "
                + " left join cloud_vendor c on a.vendor_id = c.id "
                + " left join router_subnet b on a.id = b.subnet_id where a.is_deleted = 0 and b.router_id = "
                + routerId;
        } else {
            sql =
                "select a.* , z.name zone_name, r.name region_name, c.type vendor_type, d.route_table_uuid ,d.id association_id,g.display_name resource_group_name from subnet  a "
                    + " left join region r on a.vendor_id = r.vendor_id and r.region_id = a.region_id "
                    + " left join cloud_vendor c on a.vendor_id = c.id "
                    + " left join route_table_association d on a.subnet_uuid = d.subnet_uuid and a.vendor_id = d.vendor_id and d.is_deleted = 0"
                    + " left join zone z on a.zone = z.zone_id and z.vendor_id = a.vendor_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 (hasIpv6) {
                sql += " and a.ipv6_cidr is not null";
            }
        }
        sql = QueryBuilder.buildRaw(sql, new Pager(page, rows, params, sorter), "a");
        Map<String, Object> paramMap = QueryBuilder.getParam(params);
        return this.list(Subnet.class, sql, paramMap);
    }

    public List<Subnet> listByNetworkUnSA(Long networkId) {
        String sql = "SELECT a.* FROM subnet a WHERE a.is_deleted = 0 AND a.network_id = :networkId AND a.id NOT IN (SELECT subnet_id FROM resource_subnet_relation WHERE resource_type = 'STORAGEACCOUNT')";
        Map<String, Object> paramMap = MapTools.simpleMap("networkId", networkId);
        return this.list(Subnet.class, sql, paramMap);
    }
}
