// Copyright 2018 The Terraformer Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package gcp_terraforming

import (
	"context"
	"fmt"
	"log"

	"github.com/GoogleCloudPlatform/terraformer/terraform_utils"

	"golang.org/x/oauth2/google"

	"google.golang.org/api/container/v1beta1"
)

var GkeAllowEmptyValues = []string{"labels."}

var GkeAdditionalFields = map[string]string{}

type GkeGenerator struct {
	GCPService
}

func (g *GkeGenerator) initClusters(clusters *container.ListClustersResponse, service *container.Service) []terraform_utils.Resource {
	resources := []terraform_utils.Resource{}
	for _, cluster := range clusters.Clusters {
		if _, exist := cluster.ResourceLabels["goog-composer-environment"]; exist { // don't manage composer clusters
			continue
		}
		resource := terraform_utils.NewResource(
			cluster.Name,
			cluster.Name,
			"google_container_cluster",
			"google",
			map[string]string{
				"name":    cluster.Name, // provider need cluster name as Required
				"project": g.GetArgs()["project"],
				"zone":    cluster.Zone, // provider need zone as Required
			},
			GkeAllowEmptyValues,
			GkeAdditionalFields,
		)
		resource.IgnoreKeys = append(resource.IgnoreKeys,
			"^node_pool\\.(.*)",   // delete node_pool config from google_container_cluster
			"^node_config\\.(.*)", // delete node_config config from google_container_cluster
			"^ip_allocation_policy\\.[0-9]\\.cluster_secondary_range_name$",  // conflict with cluster_ipv4_cidr_block
			"^ip_allocation_policy\\.[0-9]\\.services_secondary_range_name$", // conflict with services_ipv4_cidr_block
			"^ip_allocation_policy\\.[0-9]\\.create_subnetwork")              //only for create new cluster conflict with others ip_allocation_policy fields
		resources = append(resources, resource)
		resources = append(resources, g.initNodePools(cluster.NodePools, cluster.Name, cluster.Location, cluster.Zone)...)
	}
	return resources
}

func (g *GkeGenerator) initNodePools(nodePools []*container.NodePool, clusterName, location, zone string) []terraform_utils.Resource {
	resources := []terraform_utils.Resource{}
	for _, nodePool := range nodePools {
		resources = append(resources, terraform_utils.NewResource(
			fmt.Sprintf("%s/%s/%s", location, clusterName, nodePool.Name),
			clusterName+"_"+nodePool.Name,
			"google_container_node_pool",
			"google",
			map[string]string{
				"zone":    location,
				"project": g.GetArgs()["project"],
				"cluster": clusterName, // provider need cluster name as Required
				"name":    nodePool.Name,
			},
			GkeAllowEmptyValues,
			GkeAdditionalFields,
		))
	}
	return resources
}

// Generate TerraformResources from GCP API,
func (g *GkeGenerator) InitResources() error {
	ctx := context.Background()
	c, err := google.DefaultClient(ctx, container.CloudPlatformScope)
	if err != nil {
		log.Fatal(err)
	}

	service, err := container.New(c)
	if err != nil {
		log.Print(err)
		return err
	}
	// GKE support zone and regional cluster, api use location, it's can be region or zone, for all "-"
	location := fmt.Sprintf("projects/%s/locations/%s", g.GetArgs()["project"], "-")
	clusters, err := service.Projects.Locations.Clusters.List(location).Do()
	if err != nil {
		log.Print(err)
		return err
	}

	g.Resources = g.initClusters(clusters, service)
	g.PopulateIgnoreKeys()
	return nil
}

func (g *GkeGenerator) PostConvertHook() error {
	for i, r := range g.Resources {
		if r.InstanceInfo.Type != "google_container_node_pool" {
			continue
		}
		for _, cluster := range g.Resources {
			if cluster.InstanceState.Attributes["name"] == r.InstanceState.Attributes["cluster"] {
				g.Resources[i].Item["cluster"] = "${google_container_cluster." + cluster.InstanceState.Attributes["name"] + ".name}"
			}
		}
	}

	// hacks for fix GCP API<=>provider<=>parser inconsistency
	for i, r := range g.Resources {
		if r.InstanceInfo.Type != "google_container_cluster" {
			continue
		}
		if r.Item["master_authorized_networks_config"] != nil {
			if len(r.Item["master_authorized_networks_config"].([]interface{})) == 0 {
				g.Resources[i].Item["master_authorized_networks_config"] = map[string]interface{}{}
			}
		}
		if r.Item["ip_allocation_policy"] != nil {
			if len(r.Item["ip_allocation_policy"].([]interface{})) == 0 {
				g.Resources[i].Item["ip_allocation_policy"] = map[string]interface{}{}
			}
		}
	}
	return nil
}
