package com.ctrip.apollo.portal.service;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.ctrip.apollo.core.enums.Env;
import com.ctrip.apollo.core.dto.ItemChangeSets;
import com.ctrip.apollo.core.dto.ItemDTO;
import com.ctrip.apollo.core.dto.NamespaceDTO;
import com.ctrip.apollo.core.dto.ReleaseDTO;
import com.ctrip.apollo.core.exception.ServiceException;
import com.ctrip.apollo.core.utils.StringUtils;
import com.ctrip.apollo.portal.api.AdminServiceAPI;
import com.ctrip.apollo.portal.entity.form.NamespaceTextModel;
import com.ctrip.apollo.portal.entity.NamespaceVO;
import com.ctrip.apollo.portal.entity.form.NamespaceReleaseModel;
import com.ctrip.apollo.portal.service.txtresolver.ConfigTextResolver;
import com.fasterxml.jackson.databind.ObjectMapper;

import java.io.IOException;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

@Service
public class ConfigService {

  private Logger logger = LoggerFactory.getLogger(ConfigService.class);

  @Autowired
  private AdminServiceAPI.NamespaceAPI namespaceAPI;
  @Autowired
  private AdminServiceAPI.ItemAPI itemAPI;
  @Autowired
  private AdminServiceAPI.ReleaseAPI releaseAPI;

  @Autowired
  private ConfigTextResolver resolver;

  private ObjectMapper objectMapper = new ObjectMapper();

  /**
   * load cluster all namespace info with items
   * 
   * @param appId
   * @param env
   * @param clusterName
   * @return
   */
  public List<NamespaceVO> findNampspaces(String appId, Env env, String clusterName) {

    List<NamespaceDTO> namespaces = namespaceAPI.findNamespaceByCluster(appId, env, clusterName);
    if (namespaces == null || namespaces.size() == 0) {
      return Collections.emptyList();
    }

    List<NamespaceVO> namespaceVOs = new LinkedList<>();
    for (NamespaceDTO namespace : namespaces) {

      NamespaceVO namespaceVO = null;
      try {
        namespaceVO = parseNamespace(appId, env, clusterName, namespace);
        namespaceVOs.add(namespaceVO);
      } catch (Exception e) {
        logger.error("parse namespace error. app id:{}, env:{}, clusterName:{}, namespace:{}",
            appId, env, clusterName, namespace.getNamespaceName(), e);
        return namespaceVOs;
      }
    }

    return namespaceVOs;
  }

  @SuppressWarnings("unchecked")
  private NamespaceVO parseNamespace(String appId, Env env, String clusterName, NamespaceDTO namespace)   {
    NamespaceVO namespaceVO = new NamespaceVO();
    namespaceVO.setNamespace(namespace);

    List<NamespaceVO.ItemVO> itemVos = new LinkedList<>();
    namespaceVO.setItems(itemVos);

    String namespaceName = namespace.getNamespaceName();

    //latest createRelease
    ReleaseDTO release = releaseAPI.loadLatestRelease(appId, env, clusterName, namespaceName);
    Map<String, String> releaseItems = new HashMap<>();
    if (release != null) {
      try {
        releaseItems = objectMapper.readValue(release.getConfigurations(), Map.class);
      } catch (IOException e) {
        logger.error("parse createRelease json error. appId:{},env:{},clusterName:{},namespace:{}", appId,
                     env, clusterName, namespaceName);
        return namespaceVO;
      }
    }

    //not Release config items
    List<ItemDTO> items = itemAPI.findItems(appId, env, clusterName, namespaceName);
    int modifiedItemCnt = 0;
    for (ItemDTO itemDTO : items) {

      NamespaceVO.ItemVO itemVO = parseItemVO(itemDTO, releaseItems);

      if (itemVO.isModified()) {
        modifiedItemCnt++;
      }

      itemVos.add(itemVO);
    }
    namespaceVO.setItemModifiedCnt(modifiedItemCnt);

    return namespaceVO;
  }

  private NamespaceVO.ItemVO parseItemVO(ItemDTO itemDTO, Map<String, String> releaseItems) {
    String key = itemDTO.getKey();
    NamespaceVO.ItemVO itemVO = new NamespaceVO.ItemVO();
    itemVO.setItem(itemDTO);
    String newValue = itemDTO.getValue();
    String oldValue = releaseItems.get(key);
    if (!StringUtils.isEmpty(key) && (oldValue == null || !newValue.equals(oldValue))) {
      itemVO.setModified(true);
      itemVO.setOldValue(oldValue == null ? "" : oldValue);
      itemVO.setNewValue(newValue);
    }
    return itemVO;
  }

  /**
   * parse config text and update config items
   * 
   * @return parse result
   */
  public void updateConfigItemByText(NamespaceTextModel model) {
    String appId = model.getAppId();
    Env env = model.getEnv();
    String clusterName = model.getClusterName();
    String namespaceName = model.getNamespaceName();
    long namespaceId = model.getNamespaceId();
    String configText = model.getConfigText();

    ItemChangeSets changeSets = resolver.resolve(namespaceId, configText,
        itemAPI.findItems(appId, env, clusterName, namespaceName));
    if (changeSets.isEmpty()){
      return;
    }
    try {
      itemAPI.updateItems(appId, env, clusterName, namespaceName, changeSets);
    } catch (Exception e) {
      logger.error("itemAPI.updateItems error. appId{},env:{},clusterName:{},namespaceName:{}", appId, env, clusterName,
                   namespaceName);
      throw new ServiceException(e.getMessage());
    }

  }


  /**
   * createRelease config items
   * 
   * @return
   */
  public ReleaseDTO createRelease(NamespaceReleaseModel model) {
    return releaseAPI.release(model.getAppId(), model.getEnv(), model.getClusterName(),
        model.getNamespaceName(), model.getReleaseBy(), model.getReleaseComment());
  }
}

@@ -31,24 +31,54 @@
 l.service;%0A%0A
+import com.google.gson.Gson;%0A%0A
 import org.s
@@ -185,16 +185,60 @@
 owired;%0A
+import org.springframework.http.HttpStatus;%0A
 import o
@@ -271,24 +271,88 @@
 ype.Service;
+%0Aimport org.springframework.web.client.HttpClientErrorException;
 %0A%0Aimport com
@@ -1571,52 +1571,28 @@
 ate 
-ObjectMapper objectMapper = new ObjectMapper
+Gson gson = new Gson
 ();%0A
@@ -2953,22 +2953,16 @@
 /latest 
-create
 Release%0A
@@ -2990,76 +2990,12 @@
 e = 
-releaseAPI.loadLatestRelease(appId, env, clusterName, namespaceName)
+null
 ;%0A  
@@ -3056,20 +3056,28 @@
     
-if (
+try %7B%0A      
 release 
 != n
@@ -3076,76 +3076,114 @@
 ase 
-!
 = 
-null) %7B%0A      try %7B%0A        releaseItems = objectMapper.readValue
+releaseAPI.loadLatestRelease(appId, env, clusterName, namespaceName);%0A      releaseItems = gson.fromJson
 (rel
@@ -3228,21 +3228,31 @@
     
-  %7D 
+%7D
 catch (
-IO
+HttpClientError
 Exce
@@ -3255,33 +3255,32 @@
 Exception e)
- 
 %7B%0A      
   logger.err
@@ -3271,190 +3271,132 @@
     
-  logger.error(%22parse createRelease json error. appId:%7B%7D,env:%7B%7D,clusterName:%7B%7D,namespace:%7B%7D%22, appId,%0A                     env, clusterName, namespaceName);%0A        return namespaceVO
+if (e.getStatusCode() == HttpStatus.NOT_FOUND)%7B%0A        //ignore maybe new app has no release.%0A      %7Delse %7B%0A        throw e
 ;%0A  

load namespaces handle release NotFountException