package com.hubspot.baragon.agent.lbs;

import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import com.google.common.base.Throwables;
import com.google.common.collect.Lists;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import com.google.inject.name.Named;
import com.hubspot.baragon.agent.BaragonAgentServiceModule;
import com.hubspot.baragon.agent.config.LoadBalancerConfiguration;
import com.hubspot.baragon.agent.models.LbConfigTemplate;
import com.hubspot.baragon.exceptions.MissingTemplateException;
import com.hubspot.baragon.models.BaragonAgentMetadata;
import com.hubspot.baragon.models.BaragonConfigFile;
import com.hubspot.baragon.models.BaragonService;
import com.hubspot.baragon.models.ServiceContext;
import com.github.jknack.handlebars.Context;

@Singleton
public class LbConfigGenerator {
  private final LoadBalancerConfiguration loadBalancerConfiguration;
  private final Map<String, List<LbConfigTemplate>> templates;
  private final BaragonAgentMetadata agentMetadata;

  @Inject
  public LbConfigGenerator(LoadBalancerConfiguration loadBalancerConfiguration,
                           BaragonAgentMetadata agentMetadata,
                           @Named(BaragonAgentServiceModule.AGENT_TEMPLATES) Map<String, List<LbConfigTemplate>> templates) {
    this.loadBalancerConfiguration = loadBalancerConfiguration;
    this.agentMetadata = agentMetadata;
    this.templates = templates;
  }

  public Collection<BaragonConfigFile> generateConfigsForProject(ServiceContext snapshot) throws MissingTemplateException {
    final Collection<BaragonConfigFile> files = Lists.newArrayList();
    String templateName = snapshot.getService().getTemplateName().or(BaragonAgentServiceModule.DEFAULT_TEMPLATE_NAME);

    List<LbConfigTemplate> matchingTemplates = templates.get(templateName);

    if (templates.get(templateName) != null) {
      for (LbConfigTemplate template : matchingTemplates) {
        final List<String> filenames = getFilenames(template, snapshot.getService());

        final StringWriter sw = new StringWriter();
        final Context context = Context.newBuilder(snapshot).combine("agentProperties", agentMetadata).build();
        try {
          template.getTemplate().apply(context, sw);
        } catch (Exception e) {
          throw Throwables.propagate(e);
        }

        for (String filename : filenames) {
          files.add(new BaragonConfigFile(String.format("%s/%s", loadBalancerConfiguration.getRootPath(), filename), sw.toString()));
        }
      }
    } else {
      throw new MissingTemplateException(String.format("MissingTemplateException : Template %s could not be found", templateName));
    }

    return files;
  }

  public Set<String> getConfigPathsForProject(BaragonService service) {
    final Set<String> paths = new HashSet<>();
    for (Map.Entry<String,List<LbConfigTemplate>> entry : templates.entrySet()) {
      for (LbConfigTemplate template : entry.getValue()) {
        final List<String> filenames = getFilenames(template, service);
        for (String filename : filenames) {
          paths.add(String.format("%s/%s", loadBalancerConfiguration.getRootPath(), filename));
        }
      }
    }
    return paths;
  }

  private List<String> getFilenames(LbConfigTemplate template, BaragonService service) {
    switch (template.getFormatType()) {
      case NONE:
        return Collections.singletonList(template.getFilename());
      case SERVICE:
        return Collections.singletonList(String.format(template.getFilename(), service.getServiceId()));
      case DOMAIN_SERVICE:
      default:
        List<String> filenames = new ArrayList<>();
        if (!service.getDomains().isEmpty() && (!loadBalancerConfiguration.getDomains().isEmpty() || loadBalancerConfiguration.getDefaultDomain().isPresent())) {
          for (String domain : service.getDomains()) {
            if (isDomainServed(domain)) {
              filenames.add(String.format(template.getFilename(), domain, service.getServiceId()));
            }
          }
          if (filenames.isEmpty() && loadBalancerConfiguration.getDefaultDomain().isPresent()) {
            filenames.add(String.format(template.getFilename(), loadBalancerConfiguration.getDefaultDomain().get(), service.getServiceId()));
          } else {
            throw new IllegalStateException("No domain served for template file that requires domain");
          }
        } else if (loadBalancerConfiguration.getDefaultDomain().isPresent()){
          filenames.add(String.format(template.getFilename(), loadBalancerConfiguration.getDefaultDomain().get(), service.getServiceId()));
        } else {
          throw new IllegalStateException("No domain present for template file that requires domain");
        }
        return filenames;
    }
  }

  private boolean isDomainServed(String domain) {
    return loadBalancerConfiguration.getDomains().contains(domain) || (loadBalancerConfiguration.getDefaultDomain().isPresent() && domain.equals(loadBalancerConfiguration.getDefaultDomain().get()));
  }

}

@@ -4223,20 +4223,36 @@
 sEmpty()
- && 
+) %7B%0A            if (
 loadBala
@@ -4307,32 +4307,34 @@
 ) %7B%0A            
+  
 filenames.add(St
@@ -4449,32 +4449,34 @@
 ()));%0A          
+  
 %7D else %7B%0A       
@@ -4460,32 +4460,34 @@
        %7D else %7B%0A
+  
             thro
@@ -4566,32 +4566,46 @@
 uires domain%22);%0A
+            %7D%0A
           %7D%0A    

restructure check for empty filenames list