// 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 main

import (
	"fmt"
	"os"
	"time"

	"github.com/spf13/cobra"
	"istio.io/istio/pilot/pkg/features"
	"istio.io/istio/pkg/cmd"
	"istio.io/istio/pkg/config/constants"
	"istio.io/istio/pkg/keepalive"
	"istio.io/pkg/log"
	"istio.io/pkg/version"

	"github.com/alibaba/higress/pkg/bootstrap"
)

var (
	serverArgs     *bootstrap.ServerArgs
	loggingOptions = log.DefaultOptions()

	rootCmd = &cobra.Command{
		Use: "higress",
	}

	serveCmd = &cobra.Command{
		Use:     "serve",
		Aliases: []string{"serve"},
		Short:   "Starts the higress ingress controller",
		Example: "higress serve",
		PreRunE: func(c *cobra.Command, args []string) error {
			return log.Configure(loggingOptions)
		},
		RunE: func(c *cobra.Command, args []string) error {
			cmd.PrintFlags(c.Flags())
			log.Infof("Version %s", version.Info.String())

			stop := make(chan struct{})

			server, err := bootstrap.NewServer(serverArgs)
			if err != nil {
				return fmt.Errorf("fail to create higress server: %v", err)
			}

			if err := server.Start(stop); err != nil {
				return fmt.Errorf("fail to start higress server: %v", err)
			}

			cmd.WaitSignal(stop)

			server.WaitUntilCompletion()
			return nil
		},
	}
)

func init() {
	serverArgs = &bootstrap.ServerArgs{
		Debug:                true,
		NativeIstio:          true,
		HttpAddress:          ":8888",
		GrpcAddress:          ":15051",
		GrpcKeepAliveOptions: keepalive.DefaultOption(),
		XdsOptions: bootstrap.XdsOptions{
			DebounceAfter:     features.DebounceAfter,
			DebounceMax:       features.DebounceMax,
			EnableEDSDebounce: features.EnableEDSDebounce,
		},
	}

	serveCmd.PersistentFlags().StringVar(&serverArgs.GatewaySelectorKey, "gatewaySelectorKey", "higress", "gateway resource selector label key")
	serveCmd.PersistentFlags().StringVar(&serverArgs.GatewaySelectorValue, "gatewaySelectorValue", "higress-gateway", "gateway resource selector label value")
	serveCmd.PersistentFlags().BoolVar(&serverArgs.EnableStatus, "enableStatus", false, "enable the ingress status syncer which use to update the ip in ingress's status")
	serveCmd.PersistentFlags().StringVar(&serverArgs.IngressClass, "ingressClass", "", "if not empty, only watch the ingresses have the specified class, otherwise watch all ingresses")
	serveCmd.PersistentFlags().StringVar(&serverArgs.WatchNamespace, "watchNamespace", "", "if not empty, only wath the ingresses in the specified namespace, otherwise watch in all namespacees")
	serveCmd.PersistentFlags().BoolVar(&serverArgs.Debug, "debug", serverArgs.Debug, "if true, enables more debug http api")
	serveCmd.PersistentFlags().StringVar(&serverArgs.HttpAddress, "httpAddress", serverArgs.HttpAddress, "the http address")
	serveCmd.PersistentFlags().StringVar(&serverArgs.GrpcAddress, "grpcAddress", serverArgs.GrpcAddress, "the grpc address")
	serveCmd.PersistentFlags().BoolVar(&serverArgs.KeepStaleWhenEmpty, "keepStaleWhenEmpty", false, "keep the stale service entry when there are no endpoints in the service")
	serveCmd.PersistentFlags().StringVar(&serverArgs.RegistryOptions.ClusterRegistriesNamespace, "clusterRegistriesNamespace",
		serverArgs.RegistryOptions.ClusterRegistriesNamespace, "Namespace for ConfigMap which stores clusters configs")
	serveCmd.PersistentFlags().StringVar(&serverArgs.RegistryOptions.KubeConfig, "kubeconfig", "",
		"Use a Kubernetes configuration file instead of in-cluster configuration")
	// RegistryOptions Controller options
	serveCmd.PersistentFlags().DurationVar(&serverArgs.RegistryOptions.KubeOptions.ResyncPeriod, "resync", 60*time.Second,
		"Controller resync interval")
	serveCmd.PersistentFlags().StringVar(&serverArgs.RegistryOptions.KubeOptions.DomainSuffix, "domain", constants.DefaultKubernetesDomain,
		"DNS domain suffix")
	serveCmd.PersistentFlags().StringVar((*string)(&serverArgs.RegistryOptions.KubeOptions.ClusterID), "clusterID", "Kubernetes",
		"The ID of the cluster that this instance resides")
	serveCmd.PersistentFlags().StringToStringVar(&serverArgs.RegistryOptions.KubeOptions.ClusterAliases, "clusterAliases", map[string]string{},
		"Alias names for clusters")
	serveCmd.PersistentFlags().Float32Var(&serverArgs.RegistryOptions.KubeOptions.KubernetesAPIQPS, "kubernetesApiQPS", 80.0,
		"Maximum QPS when communicating with the kubernetes API")

	serveCmd.PersistentFlags().IntVar(&serverArgs.RegistryOptions.KubeOptions.KubernetesAPIBurst, "kubernetesApiBurst", 160,
		"Maximum burst for throttle when communicating with the kubernetes API")

	loggingOptions.AttachCobraFlags(serveCmd)
	serverArgs.GrpcKeepAliveOptions.AttachCobraFlags(serveCmd)

	rootCmd.AddCommand(serveCmd)
}

func main() {
	log.EnableKlogWithCobra()
	if err := rootCmd.Execute(); err != nil {
		log.Error(err)
		os.Exit(-1)
	}
}
