package com.bocloud.ims.service.resource;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.bocloud.cmp.driver.secuirty.FirewallDriver;
import com.bocloud.cmp.entity.enums.CloudProvider;
import com.bocloud.cmp.model.FirewallRuleModel;
import com.bocloud.common.model.*;
import com.bocloud.common.model.RequestContext.Catalog;
import com.bocloud.common.utils.Common;
import com.bocloud.common.utils.GridHelper;
import com.bocloud.common.utils.ListTool;
import com.bocloud.common.utils.MapTools;
import com.bocloud.ims.entity.resource.*;
import com.bocloud.ims.entity.resource.manageone.ManageOneAccount;
import com.bocloud.ims.interfaces.manageone.ManageOneAccountService;
import com.bocloud.ims.interfaces.resource.FirewallRuleService;
import com.bocloud.ims.interfaces.resource.VendorProjectService;
import com.bocloud.ims.repository.resource.*;
import com.bocloud.ims.service.utils.ResourceEventPublisher;
import com.bocloud.service.lock.BocloudACLock;
import com.bocloud.service.lock.LockFactory;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.TimeUnit;

/**
 * 防火墙规则service实现
 *
 * @author wangyu
 * @version 1.0
 * @since 2017年8月31日
 */
@Service("firewallRuleService")
public class FirewallRuleServiceImpl implements FirewallRuleService {
	private static Logger logger = LoggerFactory.getLogger(FirewallRuleServiceImpl.class);
	@Autowired
	private FirewallRuleRepository firewallRuleRepository;
	@Autowired
	private FirewallPolicyRepository firewallPolicyRepository;
	@Autowired
	private FirewallDriver firewallDriver;
	@Autowired
	private CloudVendorRepository cloudVendorRepository;
	@Autowired
	private OSTenantRepository osTenantRepository;
	@Autowired
	private VendorProjectService vendorProjectService;
	@Autowired
	private ResourceEventPublisher resourceEventPublisher;
	@Autowired
	private FirewallRepository firewallRepository;
	@Autowired
	private ManageOneAccountService accountService;
	@Autowired
	private LockFactory lockFactory;

	@Override
	public BocloudResult<GridBean<FirewallRule>> list(Pager pager, RequestContext context) {
		try {
			List<Param> params = Optional.ofNullable(pager.getParams()).orElse(new ArrayList<>());
			Map<String, String> sorter = Optional.ofNullable(pager.getSorter()).orElse(Maps.newHashMap());
			Boolean simple = Optional.ofNullable(pager.getSimple()).orElse(false);
			sorter.put("position", Common.ZERO);
			GridBean gridBean = null;
			Catalog catalog = context.getCatalog();
			String vendorType = null;
			if (Catalog.Tenant == catalog || Catalog.User == catalog) {
				for (Param param : params) {
					Map<String, Object> paramMap = param.getParam();
					if (paramMap.containsKey("vendorType")) {
						vendorType = String.valueOf(paramMap.get("vendorType"));
						paramMap.remove("vendorType");
					}
				}
			}
			if (0 != context.getTenant()) {
				params.add(new Param(MapTools.simpleMap(Common.TENANTID, context.getTenant()), Sign.EQ));
			}
			int total = firewallRuleRepository.count(params, vendorType);
			if (simple) {
				if (0 == context.getTenant()) {
					params.add(new Param(MapTools.simpleMap(Common.TENANTID, 0), Sign.EQ));
				}
				List<SimpleBean> beans = firewallRuleRepository.list(params, sorter);
				gridBean = new GridBean(1, 1, total, beans);
			} else {
				List<FirewallRule> list = firewallRuleRepository.list(pager.getPage(), pager.getRows(), params, sorter,
						vendorType);
				gridBean = GridHelper.getBean(pager.getPage(), pager.getRows(), total, list);
			}
			return new BocloudResult<>(true, gridBean, "查询成功");
		} catch (Exception e) {
			logger.error("list firewall rule failure:", e);
			return new BocloudResult<>(false, "查询失败");
		}
	}

	@Override
	public BocloudResult condition(String condition, RequestContext context) {
		try {
			JSONObject object = JSONObject.parseObject(condition);
			if ("listRuleByNoBoundPolicy".equalsIgnoreCase(object.getString("condition"))) {
				return this.listRuleByNoBoundPolicy(object.getLong("vendorId"), context);
			} else if ("listRuleByPolicyId".equalsIgnoreCase(object.getString("condition"))) {
				return this.listRuleByPolicyId(object.getLong("policyId"));
			}
			return null;
		} catch (Exception e) {
			logger.error("Get error message:{}", e);
			return new BocloudResult<>(false, "查询失败");
		}
	}

	@Override
	public BocloudResult<FirewallRule> create(FirewallRule rule, RequestContext context) {
		try {
			CloudVendor vendor = cloudVendorRepository.query(rule.getVendorId());
			if (null == vendor) {
				return new BocloudResult<>(false, "云平台不存在");
			}
			if (CloudProvider.MANAGEONE.name().equals(vendor.getType())) {
				return manageOneCreate(rule, vendor, context);
			}
			FirewallRule exist = firewallRuleRepository.getByName(rule.getName(), rule.getVendorId());
			if (null != exist) {
				return new BocloudResult<>(false, "防火墙规则名称已存在");
			}
			FirewallRuleModel model = JSONObject.parseObject(JSONObject.toJSONString(rule), FirewallRuleModel.class);
			BocloudResult result = null;
			TenantProvider provider = vendorProjectService.getAncestorProject(vendor.getId(), context.getTenant());
			if (null == provider) {
				result = firewallDriver.createRule(model, vendor.getUuid(), rule.getOsTenantName());
			} else {
				result = firewallDriver.createRule(model, vendor.getUuid(), provider.getProjectName());
			}
			if (result.isFailed()) {
				return new BocloudResult<>(false, result.getMessage());
			}
			model = JSONObject.parseObject(JSONObject.toJSONString(result.getData()), FirewallRuleModel.class);
			BeanUtils.copyProperties(model, rule);
			rule.preSave(context.getTarget(), context.getTarget(), rule.getVendorId(), context.getTenant(),
					context.getProject());
			rule.setId(null);
			firewallRuleRepository.save(rule);
			return new BocloudResult<>(true, rule, "创建成功!");
		} catch (Exception e) {
			logger.error("create firewall policy fail:", e);
			return new BocloudResult<>(false, "创建防火墙策略失败");
		}
	}

	private BocloudResult manageOneCreate(FirewallRule rule, CloudVendor vendor, RequestContext context)
			throws Exception {
		Firewall firewall = firewallRepository.query(rule.getFirewallId());
		if (null == firewall) {
			return new BocloudResult<>(false, "防火墙数据异常！");
		}
		new Thread(new Runnable() {
			@Override
			public void run() {
				String operate = vendor.getType().toLowerCase() + ".firewallRule";
				try {
					FirewallRuleModel model = JSONObject.parseObject(JSONObject.toJSONString(rule),
							FirewallRuleModel.class);
					model.setPolicyUuid(firewall.getPolicyUuid());
					BocloudResult result = new BocloudResult<>(false, "操作失败！");
					if (null != context.getTenant() && 0L != context.getTenant()) {
						ManageOneAccount account = accountService.queryAccount(vendor.getId(), context.getTenant());
						Assert.notNull(account, "认证失败！");
						result = firewallDriver.createRule(vendor.getUuid(), account.getUsername(),
								account.getPassword(), account.getProjectId(), account.getDomainName(), model);
					} else {
						result = firewallDriver.createRule(model, vendor.getUuid());
					}
					if (result.isFailed()) {
						resourceEventPublisher.send(new OperateResult(false, result.getMessage(), operate,
								MapTools.simpleMap("vendorId", vendor.getId()),
								OperateResult.OperateCategory.ResourceEvent, context));
						return;
					}
					model = JSONObject.parseObject(JSONObject.toJSONString(result.getData()), FirewallRuleModel.class);
					BeanUtils.copyProperties(model, rule);
					rule.setPolicyUuid(firewall.getPolicyUuid());
					rule.setPolicyId(firewall.getPolicyId());
					rule.preSave(context.getTarget(), context.getTarget(), rule.getVendorId(), context.getTenant(),
							context.getProject());
					rule.setId(null);
					rule.setVendorType(vendor.getType());
					firewallRuleRepository.save(rule);
					resourceEventPublisher.send(new OperateResult(true, "防火墙规则创建成功！", operate,
							MapTools.simpleMap("vendorId", vendor.getId()), OperateResult.OperateCategory.ResourceEvent,
							context));
				} catch (Exception e) {
					logger.error("create firewall policy fail:", e);
					resourceEventPublisher.send(new OperateResult(false, "create firewall rule fail:" + e.getMessage(),
							operate, MapTools.simpleMap("vendorId", vendor.getId()),
							OperateResult.OperateCategory.ResourceEvent, context));
				}
			}
		}).start();
		return new BocloudResult<>(true, "任务已下发，正在执行......");
	}

	@Override
	public BocloudResult<FirewallRule> modify(FirewallRule bean, Long userId) {
		String path = FirewallPolicy.class.getSimpleName() + "_" + bean.getId();
		try (BocloudACLock lock = lockFactory.getACLock(path)) {
			Assert.isTrue(lock.acquire(10, TimeUnit.SECONDS), "请求超时");
			FirewallRule rule = firewallRuleRepository.query(bean.getId());
			if (null == rule) {
				return new BocloudResult<>(false, "数据不存在");
			}
			CloudVendor vendor = cloudVendorRepository.query(rule.getVendorId());
			if (null == vendor) {
				return new BocloudResult<>(false, "云平台不存在");
			}
			// 修改防火墙规则信息时名称不能与已有名称重复
			if (!CloudProvider.MANAGEONE.name().equals(vendor.getType()) && !bean.getName().equals(rule.getName())) {
				FirewallRule exist = firewallRuleRepository.getByName(bean.getName(), vendor.getId());
				if (null != exist) {
					return new BocloudResult<>(false, "防火墙规则名称已存在");
				}
			}
			FirewallRuleModel model = JSONObject.parseObject(JSONObject.toJSONString(bean), FirewallRuleModel.class);
			model.setRuleUuid(rule.getRuleUuid());
			BocloudResult bocloudResult = firewallDriver.updateRule(model, vendor.getUuid());
			if (bocloudResult.isFailed()) {
				return new BocloudResult<>(false, bocloudResult.getMessage());
			}
			model = JSONObject.parseObject(JSONObject.toJSONString(bocloudResult.getData()), FirewallRuleModel.class);
			BeanUtils.copyProperties(model, rule);
			rule.setMenderId(userId);
			firewallRuleRepository.update(rule);
			// 如果该规则已被策略绑定，则绑定策略审计状态需要更新为否
			if (null != rule.getPolicyId() && 0L != rule.getPolicyId()) {
				FirewallPolicy policy = firewallPolicyRepository.query(rule.getPolicyId());
				if (null != policy) {
					policy.setAudited(false);// 未审计状态
					firewallPolicyRepository.update(policy);
				}
			}
			return new BocloudResult<>(true, bocloudResult.getMessage());
		} catch (Exception e) {
			logger.error("update firewall rule fail:", e);
			return new BocloudResult<>(false, "修改防火墙规则失败");
		}
	}

	@Override
	public BocloudResult<FirewallRule> detail(Long id) {
		try {
			FirewallRule rule = firewallRuleRepository.query(id);
			if (!StringUtils.isEmpty(rule.getTenantUuid())) {
				OSTenant osTenant = osTenantRepository.getByTenantUuid(rule.getTenantUuid(), rule.getVendorId());
				if (null != osTenant) {
					rule.setOsTenantName(osTenant.getName());
				}
			}
			return new BocloudResult<>(true, rule, "查询成功");
		} catch (Exception e) {
			logger.error("query firewall rule fail:", e);
			return new BocloudResult<>(false, "查询防火墙规则失败");
		}
	}

	@Override
	public BocloudResult remove(Long id, RequestContext context) {
		String path = FirewallRule.class.getSimpleName() + "_" + id;
		try (BocloudACLock lock = lockFactory.getACLock(path)) {
			Assert.isTrue(lock.acquire(10, TimeUnit.SECONDS), "请求超时");
			FirewallRule rule = firewallRuleRepository.query(id);
			if (null == rule) {
				return new BocloudResult<>(false, "数据不存在");
			}
			if (0L == context.getTenant() && 0L != rule.getTenantId()) {
				return new BocloudResult<>(false, "不能删除租户下的防火墙规则");
			}
			CloudVendor vendor = cloudVendorRepository.query(rule.getVendorId());
			if (null == vendor) {
				return new BocloudResult<>(false, "云平台不存在");
			}
			BocloudResult result = null;
			if (CloudProvider.MANAGEONE.name().equals(vendor.getType())) {
				result = firewallDriver.removeRule(vendor.getUuid(), rule.getPolicyUuid(), rule.getRuleUuid());
			} else {
				if (null != rule.getPolicyId()) {
					return new BocloudResult<>(false, "该防火墙规则正在使用，不能删除");
				}
				result = firewallDriver.removeRule(rule.getRuleUuid(), vendor.getUuid());
			}
			if (result.isFailed()) {
				return new BocloudResult<>(false, result.getMessage());
			}
			firewallRuleRepository.remove(id, context.getTarget());
			return new BocloudResult<>(true, "删除成功");
		} catch (Exception e) {
			logger.error("remove firewall rule fail:", e);
			return new BocloudResult<>(false, "删除防火墙规则失败");
		}
	}

	@Override
	public BocloudResult<List<FirewallRule>> listRuleByNoBoundPolicy(Long vendorId, RequestContext context) {
		try {
			List<FirewallRule> list = firewallRuleRepository.listRuleByNoBoundPolicy(vendorId, context.getTenant());
			return new BocloudResult<>(true, list, "查询成功");
		} catch (Exception e) {
			logger.error("list firewall rule fail:", e);
			return new BocloudResult<>(false, "查询防火墙规则失败");
		}
	}

	@Override
	public BocloudResult<List<FirewallRule>> listRuleByPolicyId(Long policyId) {
		try {
			List<FirewallRule> list = firewallRuleRepository.listRuleByPolicyId(policyId);
			return new BocloudResult<>(true, list, "查询成功");
		} catch (Exception e) {
			logger.error("list firewall rule fail:", e);
			return new BocloudResult<>(false, "查询防火墙规则失败");
		}
	}

	@Override
	public BocloudResult batchRemove(List<Long> idArray, RequestContext context) {
		if (ListTool.isEmpty(idArray)) {
			return new BocloudResult<>(false, "没有需要删除的防火墙规则！");
		}
		try {
			List<BocloudResult> succList = Lists.newArrayList();
			List<BocloudResult> failList = Lists.newArrayList();
			Long vendorId = null;
			for (Long id : idArray) {
				FirewallRule rule = firewallRuleRepository.query(id);
				if (null != rule) {
					if (null == vendorId) {
						vendorId = rule.getVendorId();
					}
					BocloudResult result = remove(id, context);
					if (result.isSuccess()) {
						succList.add(new BocloudResult<>(true, "【" + rule.getName() + "】删除成功"));
					} else {
						failList.add(new BocloudResult<>(false, "【" + rule.getName() + "】删除失败：" + result.getMessage()));
					}
				}
			}
			CloudVendor vendor = cloudVendorRepository.query(vendorId);
			String operate = vendor.getType().toLowerCase() + ".rule";
			// 删除成功的数量
			int success = succList.size();
			// 删除失败的数量
			int fail = failList.size();
			String content = null;
			if (fail == idArray.size()) {
				content = "防火墙规则批量删除失败，详情如下：\n";
				for (BocloudResult bocloudResult : failList) {
					content += bocloudResult.getMessage() + "\n";
				}
				resourceEventPublisher
						.send(new OperateResult(false, content, operate, MapTools.simpleMap("vendorId", vendor.getId()),
								OperateResult.OperateCategory.ResourceEvent, context));
			} else {
				content = "防火墙规则删除成功【" + success + "】个,失败【" + fail + "】个，详情如下：\n";
				for (BocloudResult bocloudResult : succList) {
					content += bocloudResult.getMessage() + "\n";
				}
				for (BocloudResult bocloudResult : failList) {
					content += bocloudResult.getMessage() + "\n";
				}
				resourceEventPublisher
						.send(new OperateResult(true, content, operate, MapTools.simpleMap("vendorId", vendor.getId()),
								OperateResult.OperateCategory.ResourceEvent, context));
			}
		} catch (Exception e) {
			logger.error("batch remove firewall rule fail:", e);
			return new BocloudResult<>(false, "批量删除任务下发失败");
		}
		return new BocloudResult<>(true, "批量删除任务已下发，正在执行...");
	}
}
