package com.bocloud.ims.service.resource.kubernetes;

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

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

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.bocloud.cmp.driver.kubernetes.PodDriver;
import com.bocloud.cmp.model.NotifyMessage;
import com.bocloud.cmp.model.kubernetes.ContainerModel;
import com.bocloud.cmp.model.kubernetes.PodModel;
import com.bocloud.common.model.BocloudResult;
import com.bocloud.common.model.GridBean;
import com.bocloud.common.model.OperateResult;
import com.bocloud.common.model.Pager;
import com.bocloud.common.model.Param;
import com.bocloud.common.model.RequestContext;
import com.bocloud.common.model.Sign;
import com.bocloud.common.model.SimpleBean;
import com.bocloud.common.utils.Common;
import com.bocloud.common.utils.GridHelper;
import com.bocloud.common.utils.JSONTools;
import com.bocloud.common.utils.ListTool;
import com.bocloud.common.utils.MapTools;
import com.bocloud.ims.entity.resource.CloudVendor;
import com.bocloud.ims.entity.resource.kubernetes.KubeContainer;
import com.bocloud.ims.entity.resource.kubernetes.KubeNamespace;
import com.bocloud.ims.entity.resource.kubernetes.KubeNode;
import com.bocloud.ims.entity.resource.kubernetes.KubePod;
import com.bocloud.ims.entity.resource.kubernetes.KubeService;
import com.bocloud.ims.interfaces.resource.kubernetes.KubePodService;
import com.bocloud.ims.repository.kubernetes.KubeContainerRepository;
import com.bocloud.ims.repository.kubernetes.KubeNamespaceRepository;
import com.bocloud.ims.repository.kubernetes.KubeNodeRepository;
import com.bocloud.ims.repository.kubernetes.KubePodRepository;
import com.bocloud.ims.repository.kubernetes.KubeServiceRepository;
import com.bocloud.ims.repository.resource.CloudVendorRepository;
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;

@Service("kubePodService")
public class KubePodServiceImpl implements KubePodService {

	private static final Logger logger = LoggerFactory.getLogger(KubePodServiceImpl.class);
	@Autowired
	private CloudVendorRepository cloudVendorRepository;
	@Autowired
	private KubePodRepository kubePodRepository;
	@Autowired
	private KubeServiceRepository kubeServiceRepository;
	@Autowired
	private KubeNamespaceRepository namespaceRepository;
	@Autowired
	private KubeNodeRepository kubeNodeRepository;
	@Autowired
	private LockFactory lockFactory;
	@Autowired
	private PodDriver podDriver;
	@Autowired
	private ResourceEventPublisher resourceEventPublisher;
	@Autowired
	private AmqpTemplate amqpTemplate;
	@Autowired
	private KubeContainerRepository kubeContainerRepository;

	@Override
	public BocloudResult list(Pager pager, RequestContext context) {
		GridBean gridBean = null;
		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("gmtCreate", Common.ONE);
			Integer page = Optional.ofNullable(pager.getPage()).orElse(1);
			Integer rows = Optional.ofNullable(pager.getRows()).orElse(10);

			int total = 0;
			Long tenantId = context.getTenant();
			// 为租户下的用户
			List<KubePod> list = null;
			if (null != tenantId && 0L != tenantId) {
				params.add(new Param(MapTools.simpleMap(Common.TENANTID, tenantId), Sign.EQ));
				total = this.kubePodRepository.count(params, null);
				list = this.kubePodRepository.list(page, rows, params, sorter, null);
				gridBean = GridHelper.getBean(page, rows, total, list);
			} else {
				total = this.kubePodRepository.count(params);
				if (simple) {
					List<SimpleBean> beans = this.kubePodRepository.list(params, sorter);
					gridBean = new GridBean(1, 1, total, beans);
				} else {
					list = this.kubePodRepository.list(page, rows, params, sorter);
					gridBean = GridHelper.getBean(page, rows, total, list);
				}
			}
			return new BocloudResult(true, gridBean, "查询成功");
		} catch (Exception e) {
			logger.error("Query pod list fail:", e);
			return new BocloudResult(false, "查询失败");
		}
	}

	@Override
	public BocloudResult create(KubePod pod, RequestContext context) {
		try {
			CloudVendor vendor = cloudVendorRepository.query(pod.getVendorId());
			if (null == vendor) {
				return new BocloudResult(false, "获取云供应商失败");
			}
			String yamlText = pod.getYamlText();
			if (StringUtils.isEmpty(yamlText)) {
				return new BocloudResult(false, "参数格式错误！");
			}
			PodModel model = new PodModel();
			BeanUtils.copyProperties(pod, model);
			new Thread(new Runnable() {
				@Override
				public void run() {
					try {
						BocloudResult bocloudResult = podDriver.create(vendor.getUuid(), model);
						if (bocloudResult.isFailed()) {
							resourceEventPublisher.send(new OperateResult(false, bocloudResult.getMessage(),
									"kubernetes.service", MapTools.simpleMap(Common.VENDORID, vendor.getId()),
									OperateResult.OperateCategory.ResourceEvent, context));
							return;
						}
						PodModel createModel = (PodModel) bocloudResult.getData();
						BeanUtils.copyProperties(createModel, pod);
						pod.setCreatorId(context.getTarget());
						pod.setMenderId(context.getTarget());
						pod.setVendorId(vendor.getId());
						if (StringUtils.isEmpty(createModel.getNamespace())) {
							KubeNamespace kubeNamespace = namespaceRepository.queryByName(createModel.getNamespace(),
									vendor.getId());
							if (null != kubeNamespace) {
								pod.setNsId(kubeNamespace.getId());
							}
						}
						if (StringUtils.isEmpty(createModel.getNodeName())) {
							KubeNode node = kubeNodeRepository.querByName(createModel.getNodeName(), vendor.getId());
							if (null != node) {
								pod.setNodeId(node.getId());
							}
						}
						if (StringUtils.isEmpty(createModel.getServiceName())) {
							KubeService service = kubeServiceRepository.querByName(createModel.getServiceName(),
									vendor.getId());
							if (null != service) {
								pod.setServiceId(service.getId());
							}
						}
						// 发送给云平台统计队列对云平台重新统计
						JSONObject jsonObject = new JSONObject();
						jsonObject.put(Common.VENDORID, vendor.getId());
						amqpTemplate.convertAndSend("vendor.statistics.queue", jsonObject);
						kubePodRepository.save(pod);
						syncContainers(createModel.getKubeContainers(), pod, vendor, context);
						resourceEventPublisher.send(new OperateResult(true, "创建成功！", "kubernetes.pod",
								MapTools.simpleMap(Common.VENDORID, vendor.getId()),
								OperateResult.OperateCategory.ResourceEvent, context));
					} catch (Exception e) {
						logger.error("创建失败：" + e.getMessage());
						resourceEventPublisher.send(new OperateResult(false, "创建失败：" + e.getMessage(), "kubernetes.pod",
								MapTools.simpleMap(Common.VENDORID, vendor.getId()),
								OperateResult.OperateCategory.ResourceEvent, context));
					}
				}
			}).start();
			return new BocloudResult(true, "任务已下发，正在执行...");
		} catch (Exception e) {
			logger.error("create pod error", e);
			return new BocloudResult(false, "添加失败");
		}
	}

	@Override
	public void syncContainers(List<ContainerModel> kubeContainerModels, KubePod pod, CloudVendor vendor,
			RequestContext context) throws Exception {
		if (ListTool.isEmpty(kubeContainerModels)) {
			kubeContainerRepository.remove(vendor.getId(), pod.getId(), context.getTarget());
			return;
		}
		List<KubeContainer> kubeContainers = kubeContainerRepository.listByVendor(vendor.getId(), pod.getId());
		Map<String, KubeContainer> kubeContainerMap = Maps.newHashMap();
		for (KubeContainer kubeContainer : kubeContainers) {
			String key = kubeContainer.getContainerId();
			if (kubeContainerMap.containsKey(key)) {
				kubeContainerRepository.remove(vendor.getId(), pod.getId(), context.getTarget());
			} else {
				kubeContainerMap.put(key, kubeContainer);
			}
		}
		for (ContainerModel containerModel : kubeContainerModels) {
			KubeContainer kubeContainer = new KubeContainer();
			String key = containerModel.getContainerId();
			if (kubeContainerMap.containsKey(key)) {
				kubeContainer = kubeContainerMap.get(key);
				BeanUtils.copyProperties(containerModel, kubeContainer);
				kubeContainer.setMenderId(context.getTarget());
				kubeContainer.setCreatorId(context.getTarget());
				kubeContainer.setVendorId(pod.getVendorId());
				kubeContainer.setNsId(pod.getNsId());
				kubeContainer.setNamespace(pod.getNamespace());
				kubeContainer.setNodeId(pod.getNodeId());
				kubeContainer.setNodeName(pod.getNodeName());
				kubeContainer.setServiceId(pod.getServiceId());
				kubeContainer.setServiceName(pod.getServiceName());
				kubeContainer.setPodId(pod.getId());
				kubeContainer.setPodName(pod.getName());
				kubeContainer.setAppId(pod.getAppId());
				kubeContainer.setAppName(pod.getAppName());
				kubeContainer.setDeploymentName(pod.getDeploymentName());
				kubeContainer.setTenantId(pod.getTenantId());
				kubeContainerRepository.update(kubeContainer);
				kubeContainerMap.remove(key);
				continue;
			}
			BeanUtils.copyProperties(containerModel, kubeContainer);
			kubeContainer.setMenderId(context.getTarget());
			kubeContainer.setCreatorId(context.getTarget());
			kubeContainer.setVendorId(pod.getVendorId());
			kubeContainer.setNsId(pod.getNsId());
			kubeContainer.setNamespace(pod.getNamespace());
			kubeContainer.setNodeId(pod.getNodeId());
			kubeContainer.setNodeName(pod.getNodeName());
			kubeContainer.setServiceId(pod.getServiceId());
			kubeContainer.setServiceName(pod.getServiceName());
			kubeContainer.setPodId(pod.getId());
			kubeContainer.setPodName(pod.getName());
			kubeContainer.setDeploymentName(pod.getDeploymentName());
			kubeContainer.setAppId(pod.getAppId());
			kubeContainer.setAppName(pod.getAppName());
			kubeContainer.setTenantId(pod.getTenantId());
			kubeContainerRepository.save(kubeContainer);
		}
		for (KubeContainer kubeContainer : kubeContainerMap.values()) {
			kubeContainerRepository.remove(kubeContainer.getId(), context.getTarget());
		}

	}

	@Override
	public BocloudResult modify(KubePod pod, RequestContext context) {
		String path = KubePod.class.getSimpleName() + "_" + pod.getId();
		try (BocloudACLock lock = lockFactory.getACLock(path)) {
			Assert.isTrue(lock.acquire(10, TimeUnit.SECONDS), "请求超时");
			KubePod object = this.kubePodRepository.query(pod.getId());
			if (null == object) {
				logger.warn("pod does not exist!");
				return new BocloudResult(false, "数据不存在");
			}
			CloudVendor vendor = cloudVendorRepository.query(object.getVendorId());
			if (null == vendor) {
				return new BocloudResult(false, "获取云供应商失败");
			}
			String yamlText = pod.getYamlText();
			if (StringUtils.isEmpty(yamlText)) {
				return new BocloudResult(false, "参数格式错误！");
			}
			JSONObject parseObject = JSONObject.parseObject(yamlText, JSONObject.class);
			JSONObject jsonObject = parseObject.getJSONObject("metadata");
			String newName = jsonObject.getString("name");
			String namespace = jsonObject.getString("namespace");
			if (!object.getName().equals(newName) || !object.getNamespace().equals(namespace)) {
				return new BocloudResult(false, "服务命名空间/名称不允许修改！");
			}
			PodModel model = new PodModel();
			BeanUtils.copyProperties(pod, model);
			BocloudResult bocloudResult = podDriver.modify(vendor.getUuid(), model);
			if (bocloudResult.isFailed()) {
				return new BocloudResult(false, bocloudResult.getMessage());
			}
			PodModel createModel = (PodModel) bocloudResult.getData();
			BeanUtils.copyProperties(createModel, object);
			this.kubePodRepository.update(object);
			return new BocloudResult(true, "修改成功");
		} catch (Exception e) {
			logger.error("Modify pod fail:", e);
			return new BocloudResult(false, "修改失败");
		}
	}

	@Override
	public BocloudResult remove(Long id, RequestContext context) {
		String path = KubePod.class.getSimpleName() + "_" + id;
		try (BocloudACLock lock = lockFactory.getACLock(path)) {
			Assert.isTrue(lock.acquire(10, TimeUnit.SECONDS), "请求超时");
			KubePod pod = this.kubePodRepository.query(id);
			if (null == pod) {
				return new BocloudResult(false, "数据不存在");
			}
			CloudVendor vendor = cloudVendorRepository.query(pod.getVendorId());
			if (null == vendor) {
				return new BocloudResult(false, "获取云供应商失败");
			}
			PodModel model = new PodModel();
			BeanUtils.copyProperties(pod, model);
			BocloudResult bocloudResult = podDriver.remove(model, vendor.getUuid());
			if (bocloudResult.isFailed()) {
				return new BocloudResult(false, bocloudResult.getMessage());
			}
			this.kubePodRepository.remove(id, context.getTarget());
			kubeContainerRepository.remove(vendor.getId(), id, context.getTarget());
			// 发送给云平台统计队列对云平台重新统计
			JSONObject jsonObject = new JSONObject();
			jsonObject.put(Common.VENDORID, vendor.getId());
			amqpTemplate.convertAndSend("vendor.statistics.queue", jsonObject);
			return new BocloudResult(true, "删除成功");
		} catch (Exception e) {
			logger.error("Remove pod error:", e);
			return new BocloudResult(false, "删除失败");
		}
	}

	@Override
	public BocloudResult detail(Long id) {
		try {
			KubePod pod = this.kubePodRepository.query(id);
			if (null == pod) {
				return new BocloudResult(false, "数据不存在");
			}
			return new BocloudResult(true, pod, "查询详情成功");
		} catch (Exception e) {
			logger.error("Get pod error：", e);
			return new BocloudResult(false, "查询详情失败");
		}
	}

	@Override
	public BocloudResult batchRemove(List<Long> ids, RequestContext context) {
		try {
			// pods
			if (ListTool.isEmpty(ids)) {
				return new BocloudResult(false, "请先选择需要删除的容器组！");
			}
			KubePod kubePod = kubePodRepository.query(ids.get(0));
			if (null == kubePod) {
				return new BocloudResult(false, "参数格式错误！");
			}
			CloudVendor cloudVendor = cloudVendorRepository.query(kubePod.getVendorId());
			if (null == cloudVendor) {
				return new BocloudResult(false, "平台信息异常！");
			}
			new Thread(new Runnable() {
				@Override
				public void run() {
					try {
						List<BocloudResult> succList = Lists.newArrayList();
						List<BocloudResult> failList = Lists.newArrayList();
						List<KubePod> pods = kubePodRepository.list(ids);
						PodModel model = null;
						List<Long> successIds = new ArrayList<Long>();
						for (KubePod kubePod : pods) {
							model = new PodModel();
							BeanUtils.copyProperties(kubePod, model);
							BocloudResult bocloudResult = podDriver.remove(model, cloudVendor.getUuid());
							if (bocloudResult.isSuccess()) {
								successIds.add(kubePod.getId());
								succList.add(new BocloudResult(true, "【" + kubePod.getName() + "】删除成功"));
							} else {
								failList.add(new BocloudResult(false,
										"【" + kubePod.getName() + "】删除失败：" + bocloudResult.getMessage()));
							}
						}
						if (successIds.size() > 0) {
							kubePodRepository.batchRemove(successIds, context.getTarget());
						}
						// 删除成功的数量
						int success = succList.size();
						// 删除失败的数量
						int fail = failList.size();
						String content = null;
						if (fail == ids.size()) {
							content = "容器组批量删除失败，详情如下：\n";
							// 只取一个错误的返回值
							content += failList.get(0).getMessage();
							resourceEventPublisher.send(new OperateResult(false, content, "kubernetes.pod",
									MapTools.simpleMap(Common.VENDORID, cloudVendor.getId()),
									OperateResult.OperateCategory.ResourceEvent, context));
						} else {
							content = "容器组批量删除成功【" + success + "】个,失败【" + fail + "】个！";
							if (fail > 0) {
								content += "错误信息如下：\n";
								// 只取一个错误的返回值 删除成功的就不返回信息了
								content += failList.get(0).getMessage();
							}
							resourceEventPublisher.send(new OperateResult(true, content, "kubernetes.pod",
									MapTools.simpleMap(Common.VENDORID, cloudVendor.getId()),
									OperateResult.OperateCategory.ResourceEvent, context));

						}
					} catch (Exception e) {
						resourceEventPublisher.send(new OperateResult(false, "批量删除容器组异常：" + e.getMessage(),
								"kubernetes.pod", MapTools.simpleMap(Common.VENDORID, cloudVendor.getId()),
								OperateResult.OperateCategory.ResourceEvent, context));
					}
				}
			}).start();
			return new BocloudResult(true, "批量删除任务已下发...正在执行");
		} catch (Exception e) {
			return new BocloudResult(false, "批量删除容器组异常：" + e.getMessage());
		}
	}

	@Override
	public BocloudResult apply(String params, RequestContext context) {
		try {
			JSONObject object = JSONTools.isJSONObj(params);
			if (null == object) {
				return new BocloudResult(false, "参数格式错误！");
			}
			Long vendorId = object.getLong(Common.VENDORID);
			CloudVendor cloudVendor = cloudVendorRepository.query(vendorId);
			if (null == cloudVendor) {
				return new BocloudResult(false, "云平台不存在！");
			}

			String yamlText = object.getString("yamlText");
			if (StringUtils.isEmpty(yamlText)) {
				return new BocloudResult(false, "参数格式错误！");
			}
			new Thread(new Runnable() {
				@Override
				public void run() {
					BocloudResult bocloudResult = new BocloudResult(false, "容器组申请失败！");
					try {
						PodModel model = new PodModel();
						model.setYamlText(yamlText);
						bocloudResult = podDriver.create(cloudVendor.getUuid(), model);
						if (bocloudResult.isSuccess()) {
							KubePod pod = new KubePod();
							PodModel createModel = (PodModel) bocloudResult.getData();
							BeanUtils.copyProperties(createModel, pod);
							pod.setCreatorId(context.getTarget());
							pod.setMenderId(context.getTarget());
							pod.setTenantId(context.getTenant());
							pod.setVendorId(cloudVendor.getId());
							if (StringUtils.isEmpty(createModel.getNamespace())) {
								KubeNamespace kubeNamespace = namespaceRepository
										.queryByName(createModel.getNamespace(), cloudVendor.getId());
								if (null != kubeNamespace) {
									pod.setNsId(kubeNamespace.getId());
								}
							}
							if (StringUtils.isEmpty(createModel.getNodeName())) {
								KubeNode node = kubeNodeRepository.querByName(createModel.getNodeName(),
										cloudVendor.getId());
								if (null != node) {
									pod.setNodeId(node.getId());
								}
							}
							if (StringUtils.isEmpty(createModel.getServiceName())) {
								KubeService service = kubeServiceRepository.querByName(createModel.getServiceName(),
										cloudVendor.getId());
								if (null != service) {
									pod.setServiceId(service.getId());
								}
							}
							kubePodRepository.save(pod);
							bocloudResult.setMessage("K8S Pod申请成功！容器组名称：【" + pod.getName() + "】");
						}
					} catch (Exception e) {
						logger.error("容器组申请失败：" + e.getMessage());
					}
					// 站内信
					NotifyMessage message = new NotifyMessage(context, "K8S容器组申请", bocloudResult.getMessage(), true,
							true);
					amqpTemplate.convertAndSend("event.notice.queue", message);
					resourceEventPublisher.send(new OperateResult(bocloudResult.isSuccess(), bocloudResult.getMessage(),
							"kubernetes.pod", MapTools.simpleMap(Common.VENDORID, cloudVendor.getId()),
							OperateResult.OperateCategory.ResourceEvent, context));
				}
			}).start();
			return new BocloudResult(true, "容器组申请已下发...正在执行！");
		} catch (Exception e) {
			logger.error("Get service error：", e);
			return new BocloudResult(false, e.getMessage());
		}
	}

	@Override
	public BocloudResult logDetail(Long id, RequestContext context) {
		try {
			KubePod pod = this.kubePodRepository.query(id);
			if (null == pod) {
				return new BocloudResult(false, "数据不存在");
			}
			CloudVendor cloudVendor = cloudVendorRepository.query(pod.getVendorId());
			if (null == cloudVendor) {
				return new BocloudResult(false, "云平台不存在！");
			}
			PodModel model = new PodModel();
			model.setName(pod.getName());
			model.setNamespace(pod.getNamespace());
			BocloudResult logDetail = podDriver.logDetail(cloudVendor.getUuid(), model);
			if (logDetail.isFailed()) {
				return logDetail;
			}
			return new BocloudResult(true, logDetail.getData(), "日志查询成功！");
		} catch (Exception e) {
			logger.error("Get pod error：", e);
			return new BocloudResult(false, "日志查询失败");
		}
	}
}
