package com.hubspot.baragon.service.edgecache.cloudflare;

import java.util.Collections;
import java.util.List;
import java.util.Optional;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.inject.Inject;
import com.hubspot.baragon.models.BaragonRequest;
import com.hubspot.baragon.service.edgecache.EdgeCache;
import com.hubspot.baragon.service.edgecache.cloudflare.client.CloudflareClient;
import com.hubspot.baragon.service.edgecache.cloudflare.client.CloudflareClientException;
import com.hubspot.baragon.service.edgecache.cloudflare.client.CloudflareDnsRecord;
import com.hubspot.baragon.service.edgecache.cloudflare.client.CloudflareZone;

import io.dropwizard.setup.Environment;

public class CloudflareEdgeCache implements EdgeCache {
  private static final Logger LOG = LoggerFactory.getLogger(CloudflareEdgeCache.class);

  private static final String CACHE_TAG_FORMAT = "staticjsapp-%s-%s";

  private final CloudflareClient cf;
  private final Environment environment;

  @Inject
  public CloudflareEdgeCache(CloudflareClient cf, Environment environment) {
    this.cf = cf;
    this.environment = environment;
  }

  /**
   * Invalidation will eventually occur when the TTL expires, so it's not a showstopper if this fails.
   */
  @Override
  public boolean invalidateIfNecessary(BaragonRequest request) {
    if (!request.getLoadBalancerService().getEdgeCacheDNS().isPresent()) {
      return false;
    }

    try {
      String edgeCacheDNS = request.getLoadBalancerService().getEdgeCacheDNS().get();
      Optional<CloudflareZone> matchingZone = getCloudflareZone(edgeCacheDNS);

      if (!matchingZone.isPresent()) {
        LOG.warn("`edgeCacheDNS` was defined on the request, but no matching Cloudflare Zone was found!");
        return false;
      }

      String zoneId = matchingZone.get().getId();
      Optional<CloudflareDnsRecord> matchingDnsRecord = getCloudflareDnsRecord(edgeCacheDNS, zoneId);

      if (!matchingDnsRecord.isPresent()) {
        LOG.warn("`edgeCacheDNS` was defined on the request, but no matching Cloudflare DNS Record was found!");
        return false;
      }

      if (!matchingDnsRecord.get().isProxied()) {
        LOG.warn("`edgeCacheDNS` was defined on the request, but {} is not a proxied DNS record!", edgeCacheDNS);
        return false;
      }

      return cf.purgeCache(
          zoneId,
          Collections.singletonList(                   // TODO this isn't the right way to get the env, is it vvv
              String.format(CACHE_TAG_FORMAT, request.getLoadBalancerService().getServiceId(), environment.getName())
          )
      );

    } catch (CloudflareClientException e) {
      LOG.error("Unable to invalidate Cloudflare cache for request {}", request, e);
      return false;
    }
  }

  private Optional<CloudflareDnsRecord> getCloudflareDnsRecord(String edgeCacheDNS, String zoneId) throws CloudflareClientException {
    List<CloudflareDnsRecord> dnsRecords = cf.listDnsRecords(zoneId);

    return dnsRecords.stream()
        .filter(r -> r.getZoneName().equals(edgeCacheDNS))
        .findAny();
  }

  private Optional<CloudflareZone> getCloudflareZone(String edgeCacheDNS) throws CloudflareClientException {
    List<CloudflareZone> zones = cf.getAllZones();
    String[] domainTokens = edgeCacheDNS.split("\\.");
    String baseDomain = String.format("%s.%s", domainTokens[domainTokens.length - 2], domainTokens[domainTokens.length - 1]);

    return zones.stream()
        .filter(z -> z.getName().equals(baseDomain))
        .findAny();
  }

}

@@ -270,24 +270,90 @@
 gonRequest;%0A
+import com.hubspot.baragon.service.config.EdgeCacheConfiguration;%0A
 import com.h
@@ -735,483 +735,797 @@
 e;%0A%0A
-import io.dropwizard.setup.Environment;%0A%0Apublic class CloudflareEdgeCache implements EdgeCache %7B%0A  private static final Logger LOG = LoggerFactory.getLogger(CloudflareEdgeCache.class);%0A%0A  private static final String CACHE_TAG_FORMAT = %22staticjsapp-%25s-%25s%22;%0A%0A  private final CloudflareClient cf;%0A  private final Environment environment;%0A%0A  @Inject%0A  public CloudflareEdgeCache(CloudflareClient cf, Environment environment) %7B%0A    this.cf = cf;%0A    this.environment = environment
+/**%0A * An implementation of a proxying edge cache backed by Cloudflare.%0A *%0A * Config example:%0A * ...%0A * edgeCache:%0A *   enabled: true%0A *   edgeCacheClass: CLOUDFLARE%0A *   integrationSettings:%0A *     apiBase: https://api.cloudflare.com/client/v4/%0A *     apiEmail: email@host.net%0A *     apiKey: some-key%0A *     cacheTagFormat: cache-tag-for-%25-service%0A * ...%0A */%0Apublic class CloudflareEdgeCache implements EdgeCache %7B%0A  private static final Logger LOG = LoggerFactory.getLogger(CloudflareEdgeCache.class);%0A%0A  private final CloudflareClient cf;%0A  private final EdgeCacheConfiguration edgeCacheConfiguration;%0A%0A  @Inject%0A  public CloudflareEdgeCache(CloudflareClient cf, EdgeCacheConfiguration edgeCacheConfiguration) %7B%0A    this.cf = cf;%0A    this.edgeCacheConfiguration = edgeCacheConfiguration
 ;%0A  
@@ -2792,16 +2792,17 @@
 tonList(
+%0A
         
@@ -2811,117 +2811,129 @@
     
-     // TODO this isn't the right way to get the env, is it vvv%0A              String.format(CACHE_TAG_FORMAT,
+String.format(%0A                  edgeCacheConfiguration.getIntegrationSettings().get(%22cacheTagFormat%22),%0A                 
  req
@@ -2980,31 +2980,23 @@
 Id()
-, environment.getName()
+%0A              
 )%0A  

Make cache tag format directly configurable.