// Copyright 2020 Envoyproxy 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 model

import (
	"encoding/json"
	"strconv"
	"strings"

	networking "istio.io/api/networking/v1alpha3"
	istiomodel "istio.io/istio/pilot/pkg/model"
	"istio.io/istio/pkg/config/host"
)

// TrafficDirection defines whether traffic exists a service instance or enters a service instance
type TrafficDirection string

const (
	// TrafficDirectionInbound indicates inbound traffic
	TrafficDirectionInbound TrafficDirection = "inbound"
	// TrafficDirectionOutbound indicates outbound traffic
	TrafficDirectionOutbound TrafficDirection = "outbound"
)

// BuildClusterName the cluster name referencing service instances for a given service name, a subset and a port,
// which is the same as the cluster name generated by Istio.
func BuildClusterName(direction TrafficDirection, subsetName, hostname string, port int) string {
	if direction == TrafficDirectionInbound {
		// On 1.8+ Proxies, Istio uses format inbound|port||. Telemetry no longer requires the hostname
		return istiomodel.BuildSubsetKey(istiomodel.TrafficDirection(direction), subsetName, "", port)
	}
	return istiomodel.BuildSubsetKey(istiomodel.TrafficDirection(direction), subsetName, host.Name(hostname), port)
}

// BuildMetaProtocolRouteName the route name for a given metaProtocol service
func BuildMetaProtocolRouteName(host string, port int) string {
	return host + "_" + strconv.Itoa(port)
}

// GetHashPolicy return consistent hash policy in dr
// it will be overridden if subset named as subsetName in param is not nil
func GetHashPolicy(dr *DestinationRuleWrapper, subsetName string) string {
	if subsetName == "" {
		if dr != nil && dr.Spec != nil && dr.Spec.TrafficPolicy != nil {
			return getConsistentHashHeaderName(dr.Spec.TrafficPolicy)
		}
	} else if dr != nil && dr.Spec != nil && dr.Spec.Subsets != nil {
		for _, subset := range dr.Spec.Subsets {
			if subsetName == subset.GetName() {
				return getConsistentHashHeaderName(subset.TrafficPolicy)
			}
		}
	}
	return ""
}

// getConsistentHashHeaderName return consistent hash header in TrafficPolicy
func getConsistentHashHeaderName(tp *networking.TrafficPolicy) string {
	if tp != nil && tp.LoadBalancer != nil && tp.LoadBalancer.GetConsistentHash() != nil {
		return tp.LoadBalancer.GetConsistentHash().GetHttpHeaderName()
	}
	return ""
}

// Struct2JSON convert a go struct to a json object
func Struct2JSON(ojb interface{}) interface{} {
	b, err := json.Marshal(ojb)
	if err != nil {
		return ojb
	}
	return string(b)
}

// IsFQDNEquals return true if two FQDN name equals, false if not
func IsFQDNEquals(name1, ns1, name2, ns2 string) bool {
	var fqdn1 = name1
	var fqdn2 = name2
	if !strings.Contains(name1, ".") {
		fqdn1 = name1 + "." + ns1 + ".svc.cluster.local"
	}
	if !strings.Contains(name2, ".") {
		fqdn2 = name2 + "." + ns2 + ".svc.cluster.local"
	}
	return fqdn1 == fqdn2
}
