// Copyright 2020 Lingfei Kong <colin404@foxmail.com>. All rights reserved.
// Use of this source code is governed by a MIT style
// license that can be found in the LICENSE file.

package sync

import (
	"context"
	"fmt"

	genericoptions "github.com/pingcap-inc/it-automation/internal/pkg/options"
	genericapiserver "github.com/pingcap-inc/it-automation/internal/pkg/server"
	"github.com/pingcap-inc/it-automation/internal/sync-google-email/config"
	"github.com/pingcap-inc/it-automation/internal/sync-google-email/store"
	"github.com/pingcap-inc/it-automation/internal/sync-google-email/store/mysql"
	"github.com/pingcap-inc/it-automation/pkg/log"
	"github.com/pingcap-inc/it-automation/pkg/shutdown"
	"github.com/pingcap-inc/it-automation/pkg/shutdown/shutdownmanagers/posixsignal"
	"github.com/pingcap-inc/it-automation/pkg/storage"
)

type apiServer struct {
	gs           *shutdown.GracefulShutdown
	redisOptions *genericoptions.RedisOptions
	// gRPCAPIServer    *grpcAPIServer
	genericAPIServer *genericapiserver.GenericAPIServer
}

type preparedAPIServer struct {
	*apiServer
}

// ExtraConfig defines extra configuration for the iam-sync-google-email.
type ExtraConfig struct {
	Addr         string
	MaxMsgSize   int
	ServerCert   genericoptions.GeneratableKeyCert
	mysqlOptions *genericoptions.MySQLOptions
	// etcdOptions      *genericoptions.EtcdOptions
}

func createAPIServer(cfg *config.Config) (*apiServer, error) {
	gs := shutdown.New()
	gs.AddShutdownManager(posixsignal.NewPosixSignalManager())

	genericConfig, err := buildGenericConfig(cfg)
	if err != nil {
		return nil, err
	}

	extraConfig, err := buildExtraConfig(cfg)
	if err != nil {
		return nil, err
	}

	storeIns, _ := mysql.GetMySQLFactoryOr(extraConfig.mysqlOptions)
	store.SetClient(storeIns)

	genericServer, err := genericConfig.Complete().New()
	if err != nil {
		return nil, err
	}
	// extraServer, err := extraConfig.complete().New()
	if err != nil {
		return nil, err
	}

	server := &apiServer{
		gs:               gs,
		redisOptions:     cfg.RedisOptions,
		genericAPIServer: genericServer,
		// gRPCAPIServer:    extraServer,
	}

	return server, nil
}

func (s *apiServer) PrepareRun() preparedAPIServer {
	// initRouter(s.genericAPIServer.Engine)

	s.initRedisStore()
	initRouter(s.genericAPIServer.Engine)
	initCron()
	s.gs.AddShutdownCallback(shutdown.ShutdownFunc(func(string) error {
		mysqlStore, _ := mysql.GetMySQLFactoryOr(nil)
		if mysqlStore != nil {
			_ = mysqlStore.Close()
		}

		// s.gRPCAPIServer.Close()
		s.genericAPIServer.Close()

		return nil
	}))

	return preparedAPIServer{s}
}

func (s preparedAPIServer) Run() error {
	// go s.gRPCAPIServer.Run()

	// start shutdown managers
	if err := s.gs.Start(); err != nil {
		log.Fatalf("start shutdown manager failed: %s", err.Error())
	}

	return s.genericAPIServer.Run()
}

// nolint:unused
type completedExtraConfig struct {
	*ExtraConfig
}

// nolint:unused
// Complete fills in any fields not set that are required to have valid data and can be derived from other fields.
func (c *ExtraConfig) complete() *completedExtraConfig {
	if c.Addr == "" {
		c.Addr = "127.0.0.1:8081"
	}

	return &completedExtraConfig{c}
}

// New create a grpcAPIServer instance.
// func (c *completedExtraConfig) New() (*grpcAPIServer, error) {
// 	creds, err := credentials.NewServerTLSFromFile(c.ServerCert.CertKey.CertFile, c.ServerCert.CertKey.KeyFile)
// 	if err != nil {
// 		log.Fatalf("Failed to generate credentials %s", err.Error())
// 	}
// 	opts := []grpc.ServerOption{grpc.MaxRecvMsgSize(c.MaxMsgSize), grpc.Creds(creds)}
// 	grpcServer := grpc.NewServer(opts...)
//
// 	storeIns, _ := mysql.GetMySQLFactoryOr(c.mysqlOptions)
// 	// storeIns, _ := etcd.GetEtcdFactoryOr(c.etcdOptions, nil)
// 	store.SetClient(storeIns)
// 	cacheIns, err := cachev1.GetCacheInsOr(storeIns)
// 	if err != nil {
// 		log.Fatalf("Failed to get cache instance: %s", err.Error())
// 	}
//
// 	pb.RegisterCacheServer(grpcServer, cacheIns)
//
// 	reflection.Register(grpcServer)
//
// 	return &grpcAPIServer{grpcServer, c.Addr}, nil
// }

func buildGenericConfig(cfg *config.Config) (genericConfig *genericapiserver.Config, lastErr error) {
	genericConfig = genericapiserver.NewConfig()
	if lastErr = cfg.GenericServerRunOptions.ApplyTo(genericConfig); lastErr != nil {
		return
	}

	if lastErr = cfg.FeatureOptions.ApplyTo(genericConfig); lastErr != nil {
		return
	}

	if lastErr = cfg.SecureServing.ApplyTo(genericConfig); lastErr != nil {
		return
	}

	if lastErr = cfg.InsecureServing.ApplyTo(genericConfig); lastErr != nil {
		return
	}

	return
}

// nolint: unparam
func buildExtraConfig(cfg *config.Config) (*ExtraConfig, error) {
	return &ExtraConfig{
		Addr:         fmt.Sprintf("%s:%d", cfg.GRPCOptions.BindAddress, cfg.GRPCOptions.BindPort),
		MaxMsgSize:   cfg.GRPCOptions.MaxMsgSize,
		ServerCert:   cfg.SecureServing.ServerCert,
		mysqlOptions: cfg.MySQLOptions,
		// etcdOptions:      cfg.EtcdOptions,
	}, nil
}

func (s *apiServer) initRedisStore() {
	ctx, cancel := context.WithCancel(context.Background())
	s.gs.AddShutdownCallback(shutdown.ShutdownFunc(func(string) error {
		cancel()

		return nil
	}))

	config := &storage.Config{
		Host:                  s.redisOptions.Host,
		Port:                  s.redisOptions.Port,
		Addrs:                 s.redisOptions.Addrs,
		MasterName:            s.redisOptions.MasterName,
		Username:              s.redisOptions.Username,
		Password:              s.redisOptions.Password,
		Database:              s.redisOptions.Database,
		MaxIdle:               s.redisOptions.MaxIdle,
		MaxActive:             s.redisOptions.MaxActive,
		Timeout:               s.redisOptions.Timeout,
		EnableCluster:         s.redisOptions.EnableCluster,
		UseSSL:                s.redisOptions.UseSSL,
		SSLInsecureSkipVerify: s.redisOptions.SSLInsecureSkipVerify,
	}

	// try to connect to redis
	go storage.ConnectToRedis(ctx, config)
}

func initCron() {
	// google user check
	// service.CheckGoogleUsers(context.Background())
	//
	// gucJob := cron.NewTaskGroup("google-user-check")
	// err := gucJob.Add("0 10 12 * * *", func() {
	// 	service.CheckGoogleUsers(context.Background())
	// })
	// if err != nil {
	// 	log.Errorf("add google-user-check task failed: %s", err.Error())
	// } else {
	// 	gucJob.Start()
	// }
	//
	// service.DeptCompare(context.Background())
	// dcJob := cron.NewTaskGroup("department-compare")
	// err = dcJob.Add("0 0 8,12,16 * * *", func() {
	// 	service.DeptCompare(context.Background())
	// })
	// if err != nil {
	// 	log.Errorf("add department-compare task failed: %s", err.Error())
	// } else {
	// 	dcJob.Start()
	// }
	//
	// service.Sync()
	// gmJob := cron.NewTaskGroup("sge-server")
	// err = gmJob.Add("0 10 8,12,16 * * *", func() {
	// 	service.Sync()
	// })
	// if err != nil {
	// 	log.Errorf("add sge-server task failed: %s", err.Error())
	// } else {
	// 	gmJob.Start()
	// }
	//
	// service.GroupOpen()
	// goJob := cron.NewTaskGroup("group-open")
	// err = goJob.Add("0 20 8,12,16 * * *", func() {
	// 	service.GroupOpen()
	// })
	// if err != nil {
	// 	log.Errorf("add group-open task failed: %s", err.Error())
	// } else {
	// 	goJob.Start()
	// }
	//
	// sync.SyncDeptUser()
	// sauJob := cron.NewTaskGroup("sync-authing-user")
	// err = sauJob.Add("0 25 8,12,16 * * *", func() {
	// 	sync.SyncDeptUser()
	// })
	// if err != nil {
	// 	log.Errorf("add sync-authing-user task failed: %s", err.Error())
	// } else {
	// 	sauJob.Start()
	// }
	//
	// service.CheckRegionEmail(context.Background())
	// coeJob := cron.NewTaskGroup("check-office-email")
	// err = coeJob.Add("0 30 8,12,16 * * *", func() {
	// 	service.CheckRegionEmail(context.Background())
	// })
	// if err != nil {
	// 	log.Errorf("add check-office-email task failed: %s", err.Error())
	// } else {
	// 	coeJob.Start()
	// }

}
