// Copyright (c) 2022 Alibaba Group Holding Ltd.
//
// 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 common

import (
	"strings"
	"time"

	networking "istio.io/api/networking/v1alpha3"
	"istio.io/istio/pilot/pkg/model"
	"istio.io/istio/pkg/cluster"
	"istio.io/istio/pkg/config"
	gatewaytool "istio.io/istio/pkg/config/gateway"
	listerv1 "k8s.io/client-go/listers/core/v1"
	"k8s.io/client-go/tools/cache"

	"github.com/alibaba/higress/v2/pkg/cert"
	"github.com/alibaba/higress/v2/pkg/common"
	"github.com/alibaba/higress/v2/pkg/ingress/kube/annotations"
)

type ServiceKey struct {
	Namespace   string
	Name        string
	ServiceFQDN string
	Port        int32
}

type WrapperConfig struct {
	Config            *config.Config
	AnnotationsConfig *annotations.Ingress
}

type WrapperConfigWithRuleKey struct {
	Config  *config.Config
	RuleKey string
}

type WrapperGateway struct {
	Gateway       *networking.Gateway
	WrapperConfig *WrapperConfig
	ClusterId     cluster.ID
	Host          string
}

func CreateMcpServiceKey(host string, portNumber int32) ServiceKey {
	return ServiceKey{
		Namespace:   "mcp",
		Name:        host,
		ServiceFQDN: host,
		Port:        portNumber,
	}
}

func (w *WrapperGateway) IsHTTPS() bool {
	if w.Gateway == nil || len(w.Gateway.Servers) == 0 {
		return false
	}

	for _, server := range w.Gateway.Servers {
		if gatewaytool.IsTLSServer(server) {
			return true
		}
	}

	return false
}

type WrapperHTTPRoute struct {
	HTTPRoute        *networking.HTTPRoute
	WrapperConfig    *WrapperConfig
	RawClusterId     string
	ClusterId        cluster.ID
	ClusterName      string
	Host             string
	OriginPath       string
	OriginPathType   PathType
	WeightTotal      int32
	IsDefaultBackend bool
	RuleKey          string
}

func (w *WrapperHTTPRoute) Meta() string {
	return strings.Join([]string{w.WrapperConfig.Config.Namespace, w.WrapperConfig.Config.Name}, "/")
}

func (w *WrapperHTTPRoute) BasePathFormat() string {
	return strings.Join([]string{w.Host, w.OriginPath}, "-")
}

func (w *WrapperHTTPRoute) PathFormat() string {
	return strings.Join([]string{w.Host, string(w.OriginPathType), w.OriginPath}, "-")
}

type WrapperVirtualService struct {
	VirtualService           *networking.VirtualService
	WrapperConfig            *WrapperConfig
	ConfiguredDefaultBackend bool
	AppRoot                  string
}

type WrapperTrafficPolicy struct {
	TrafficPolicy     *networking.TrafficPolicy
	PortTrafficPolicy *networking.TrafficPolicy_PortTrafficPolicy
	WrapperConfig     *WrapperConfig
}

type WrapperDestinationRule struct {
	DestinationRule *networking.DestinationRule
	WrapperConfig   *WrapperConfig
	ServiceKey      ServiceKey
}

type ServiceProxyConfig struct {
	ProxyName        string
	UpstreamProtocol common.Protocol
	UpstreamSni      string
}

type ServiceWrapper struct {
	ServiceName            string
	ServiceEntry           *networking.ServiceEntry
	DestinationRuleWrapper *WrapperDestinationRule
	Suffix                 string
	RegistryType           string
	RegistryName           string
	ProxyConfig            *ServiceProxyConfig
	createTime             time.Time
}

func (sew *ServiceWrapper) DeepCopy() *ServiceWrapper {
	res := &ServiceWrapper{}
	*res = *sew
	res.ServiceEntry = sew.ServiceEntry.DeepCopy()

	if sew.DestinationRuleWrapper != nil {
		res.DestinationRuleWrapper = sew.DestinationRuleWrapper
		res.DestinationRuleWrapper.DestinationRule = sew.DestinationRuleWrapper.DestinationRule.DeepCopy()
	}
	return res
}

func (sew *ServiceWrapper) SetCreateTime(createTime time.Time) {
	sew.createTime = createTime
}

func (sew *ServiceWrapper) GetCreateTime() time.Time {
	return sew.createTime
}

type ProxyWrapper struct {
	ProxyName    string
	ListenerPort uint32
	EnvoyFilter  *networking.EnvoyFilter
	createTime   time.Time
}

func (pw *ProxyWrapper) DeepCopy() *ProxyWrapper {
	res := &ProxyWrapper{}
	*res = *pw

	if pw.EnvoyFilter != nil {
		res.EnvoyFilter = pw.EnvoyFilter.DeepCopy()
	}
	return res
}

func (pw *ProxyWrapper) SetCreateTime(createTime time.Time) {
	pw.createTime = createTime
}

func (pw *ProxyWrapper) GetCreateTime() time.Time {
	return pw.createTime
}

type IngressController interface {
	// RegisterEventHandler adds a handler to receive config update events for a
	// configuration type
	RegisterEventHandler(kind config.GroupVersionKind, handler model.EventHandler)

	List() []config.Config

	ServiceLister() listerv1.ServiceLister

	SecretLister() listerv1.SecretLister

	ConvertGateway(convertOptions *ConvertOptions, wrapper *WrapperConfig, httpsCredentialConfig *cert.Config) error

	ConvertHTTPRoute(convertOptions *ConvertOptions, wrapper *WrapperConfig) error

	ApplyDefaultBackend(convertOptions *ConvertOptions, wrapper *WrapperConfig) error

	ApplyCanaryIngress(convertOptions *ConvertOptions, wrapper *WrapperConfig) error

	ConvertTrafficPolicy(convertOptions *ConvertOptions, wrapper *WrapperConfig) error

	// Run until a signal is received
	Run(stop <-chan struct{})

	SetWatchErrorHandler(func(r *cache.Reflector, err error)) error

	// HasSynced returns true after initial cache synchronization is complete
	HasSynced() bool
}

type KIngressController interface {
	// RegisterEventHandler adds a handler to receive config update events for a
	// configuration type
	RegisterEventHandler(kind config.GroupVersionKind, handler model.EventHandler)

	List() []config.Config

	ServiceLister() listerv1.ServiceLister

	SecretLister() listerv1.SecretLister

	ConvertGateway(convertOptions *ConvertOptions, wrapper *WrapperConfig) error

	ConvertHTTPRoute(convertOptions *ConvertOptions, wrapper *WrapperConfig) error

	// Run until a signal is received
	Run(stop <-chan struct{})

	SetWatchErrorHandler(func(r *cache.Reflector, err error)) error

	// HasSynced returns true after initial cache synchronization is complete
	HasSynced() bool
}

type GatewayController interface {
	model.ConfigStoreController

	SetWatchErrorHandler(func(r *cache.Reflector, err error)) error
}
