// Copyright 2017 PingCAP, Inc.
//
// 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,
// See the License for the specific language governing permissions and
// limitations under the License.package spec

package main

import (
	"flag"
	"net/http"
	"os"
	"time"

	restful "github.com/emicklei/go-restful"
	"github.com/emicklei/go-restful-swagger12"
	"github.com/ngaut/log"
	"github.com/pingcap/tidb-cloud-manager/pkg/client"
	"github.com/pingcap/tidb-cloud-manager/pkg/crd"
	"github.com/pingcap/tidb-cloud-manager/pkg/manager"
	"github.com/pingcap/tidb-cloud-manager/pkg/server"
	"github.com/pingcap/tidb-cloud-manager/pkg/tidbinstance/controller"
	"github.com/pingcap/tidb-cloud-manager/pkg/version"
	"github.com/pingcap/private-tidb-operator/pkg/util/election"
	"github.com/pingcap/private-tidb-operator/pkg/util/election/resourcelock"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/util/wait"
	"k8s.io/client-go/tools/record"
)

var (
	logLevel                string
	repoPrefix              string
	serviceType             string
	webServicesURL          string
	namespace               string
	printVersion            bool
	kubeconfig              string
	alertmanagerURL         string
	backupDir               string
	exposePrivilegedTiDBSVC bool
	rbacEnabled             bool

	leaseDuration = 15 * time.Second
	renewDuration = 5 * time.Second
	retryPeriod   = 3 * time.Second
)

func init() {
	flag.StringVar(&logLevel, "L", "info", "log level: debug, info, warn, error, fatal")
	flag.StringVar(&repoPrefix, "repo-prefix", "pingcap", "docker repo prefix for tidb related images")
	flag.StringVar(&serviceType, "service-type", "NodePort", "service type(NodePort, ClusterIP, LoadBalancer) for tidb")
	flag.StringVar(&webServicesURL, "web-services-url", "http://tidb-cloud-manager:32333", "URL that can be accessed through an external network")
	flag.BoolVar(&printVersion, "V", false, "Show version and quit")
	flag.BoolVar(&printVersion, "version", false, "Show version and quit")
	flag.StringVar(&kubeconfig, "kubeconfig", "", "Path to kubeconfig file, omit this if run in cluster")
	flag.StringVar(&alertmanagerURL, "alertmanager-url", "", "alertmanager URL for prometheus, for example http://alertmanager.kube-system:9093")
	flag.StringVar(&backupDir, "backup-dir", "/data", "Backup directory on node")
	flag.BoolVar(&exposePrivilegedTiDBSVC, "expose-privileged-tidb-svc", false, "Whether to expose privileged TiDB SVC")
	flag.BoolVar(&rbacEnabled, "rbac-enabled", false, "Whether k8s cluster RBAC is enabled")

	flag.Parse()
	log.SetLevelByString(logLevel)
	log.SetHighlighting(false)
	log.SetFlags(log.Ldate | log.Lmicroseconds | log.Lshortfile)
}

func main() {
	if printVersion {
		version.PrintVersionInfo()
		os.Exit(0)
	}
	version.LogVersionInfo()

	ns := os.Getenv("MY_POD_NAMESPACE")
	if ns == "" {
		log.Fatal("MY_POD_NAMESPACE environment variable not set")
	}

	manager.BackupDir = backupDir
	manager.RepoPrefix = repoPrefix
	m := manager.New(kubeconfig, serviceType, ns)

	svrManager := server.Manager{m}
	ws := svrManager.NewService()
	restful.Add(ws)
	config := swagger.Config{
		WebServices:     restful.DefaultContainer.RegisteredWebServices(),
		WebServicesUrl:  webServicesURL,
		ApiPath:         "/apidocs.json",
		SwaggerPath:     "/apidocs/",
		SwaggerFilePath: "/usr/local/apidocs",
	}
	swagger.RegisterSwaggerService(config, restful.DefaultContainer)

	hostname, err := os.Hostname()
	if err != nil {
		log.Fatalf("failed to get hostname: %v", err)
	}
	if err := crd.EnsureCRDExists(kubeconfig); err != nil {
		log.Fatalf("failed to ensure CRD exists: %v", err)
	}
	cli, err := client.New(kubeconfig)
	if err != nil {
		log.Fatalf("failed to get client: %v", err)
	}
	rl := resourcelock.EndpointsLock{
		EndpointsMeta: metav1.ObjectMeta{
			Namespace: ns,
			Name:      "tidb-cloud-manager",
		},
		Client: cli,
		LockConfig: resourcelock.ResourceLockConfig{
			Identity:      hostname,
			EventRecorder: &record.FakeRecorder{},
		},
	}

	duration := 5 * time.Second

	controller.AlertmanagerURL = alertmanagerURL
	controller.ExposePrivilegedTiDBSVC = exposePrivilegedTiDBSVC
	controller.RBACEnabled = rbacEnabled
	go wait.Forever(func() {
		election.RunOrDie(election.LeaderElectionConfig{
			Lock:          &rl,
			LeaseDuration: leaseDuration,
			RenewDeadline: renewDuration,
			RetryPeriod:   retryPeriod,
			Callbacks: election.LeaderCallbacks{
				OnStartedLeading: func(stopCh <-chan struct{}) {
					wait.Forever(func() { controller.NewTiDBInstanceController(ns, cli).Run(stopCh) }, duration)
				},
				OnStoppedLeading: func() {
					log.Fatalf("leader election lost")
				},
			},
		})
	}, duration)

	log.Infof("tidb-cloud-manager server listening on 0.0.0.0:2333")
	log.Fatal(http.ListenAndServe(":2333", nil))
}
