package com.airlock.iam.plugin.framework.infrastructure.xstream;

import com.airlock.iam.plugin.framework.domain.model.ConfigHistoryMetaDataItem;
import com.airlock.iam.plugin.framework.domain.model.ConfigTemplateMetaData;
import com.airlock.iam.plugin.framework.domain.model.MedusaConfigurationElement;
import com.airlock.iam.plugin.framework.domain.model.Plugin;
import com.airlock.iam.system.version.ConfigSchemaVersion;
import com.thoughtworks.xstream.converters.ConversionException;
import com.thoughtworks.xstream.converters.Converter;
import com.thoughtworks.xstream.converters.MarshallingContext;
import com.thoughtworks.xstream.converters.UnmarshallingContext;
import com.thoughtworks.xstream.io.HierarchicalStreamReader;
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
import java.util.List;
import org.apache.commons.lang3.StringUtils;

class MedusaConfigurationElementConverter implements Converter {
   static final String TAG_MEDUSA_CONFIGURATION = "medusaConfiguration";
   private static final String PROP_MEDUSA_VERSION = "medusa-version";
   private static final String PROP_MEDUSA_TEMPLATE_NAME = "templateName";
   private static final String PROP_MEDUSA_TEMPLATE_DESCRIPTION = "templateDescription";
   private static final String PROP_MEDUSA_TEMPLATE_TAGS = "templateTags";
   private static final String TAG_BASE_IDENTIFIER = "baseConfiguration";
   private static final String TAG_HISTORY_CONTAINER = "configHistoryMetaData";

   public boolean canConvert(Class type) {
      return MedusaConfigurationElement.class.equals(type);
   }

   public void marshal(Object source, HierarchicalStreamWriter writer, MarshallingContext context) {
      MedusaConfigurationElement config = (MedusaConfigurationElement)source;
      this.writeToplevelNode(writer, config);
      this.writePlugins(context, config.getToplevelPlugins());
      this.writeHistory(writer, context, config.getHistory());
   }

   private void writeToplevelNode(HierarchicalStreamWriter writer, MedusaConfigurationElement config) {
      writer.addAttribute("xmlns", "http://www.ergon.ch");
      writer.addAttribute("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance");
      writer.addAttribute("xsi:schemaLocation", "http://www.ergon.ch medusa-config.xsd");
      XStreamHelper.writeAttributeIfValueIsPresent(writer, "medusa-version", config.getIamVersion());
   }

   private void writePlugins(MarshallingContext context, List<Plugin> toplevelPlugins) {
      for (Plugin plugin : toplevelPlugins) {
         context.convertAnother(plugin);
      }
   }

   private void writeHistory(HierarchicalStreamWriter writer, MarshallingContext context, List<ConfigHistoryMetaDataItem> history) {
      if (!history.isEmpty()) {
         writer.startNode("configHistoryMetaData");

         for (ConfigHistoryMetaDataItem historyItem : history) {
            context.convertAnother(historyItem);
         }

         writer.endNode();
      }
   }

   public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) {
      XStreamHelper.ensureInNode("medusaConfiguration", reader);
      MedusaConfigurationElement config = new MedusaConfigurationElement();
      config.setIamVersion(reader.getAttribute("medusa-version"));
      config.setSchemaVersion(ConfigSchemaVersion.XML_CONFIG_SCHEMA);
      String templateName = reader.getAttribute("templateName");
      String templateDescription = reader.getAttribute("templateDescription");
      String templateTags = reader.getAttribute("templateTags");
      if (StringUtils.isNotBlank(templateName) && StringUtils.isNotBlank(templateDescription)) {
         config.setTemplateMetaData(new ConfigTemplateMetaData(templateName, templateDescription, StringUtils.split(templateTags, ",")));
      }

      while (reader.hasMoreChildren()) {
         reader.moveDown();
         this.parseInnerNodes(reader, context, config);
         reader.moveUp();
      }

      return config;
   }

   private void parseInnerNodes(HierarchicalStreamReader reader, UnmarshallingContext context, MedusaConfigurationElement config) {
      if ("plugin".equals(reader.getNodeName())) {
         config.addToplevelPlugin((Plugin)context.convertAnother(config, Plugin.class));
      } else {
         if ("baseConfiguration".equals(reader.getNodeName())) {
            throw new ConversionException(
               "'baseConfiguration' is not supported any longer. Please refer to the documentation on how to migrate your configuration to use configuration environments."
            );
         }

         if (!"configHistoryMetaData".equals(reader.getNodeName())) {
            throw new ConversionException(reader.getNodeName() + " is unknown");
         }

         this.parseHistory(reader, context, config);
      }
   }

   private void parseHistory(HierarchicalStreamReader reader, UnmarshallingContext context, MedusaConfigurationElement config) {
      while (reader.hasMoreChildren()) {
         reader.moveDown();
         config.addHistoryItem((ConfigHistoryMetaDataItem)context.convertAnother(config, ConfigHistoryMetaDataItem.class));
         reader.moveUp();
      }
   }
}
