package main

import (
	"context"
	"crypto/rsa"
	"crypto/x509"
	"encoding/pem"
	"flag"
	"fmt"
	"os"

	aws2 "github.com/pingcap-inc/clinic/pkg/aws"
	"github.com/pingcap-inc/clinic/pkg/resource_limit"

	"github.com/gin-contrib/pprof"
	"github.com/gin-gonic/gin"
	"github.com/pingcap/errors"
	"github.com/pingcap/log"
	"go.uber.org/zap"

	"github.com/pingcap-inc/clinic/pkg/api"
	"github.com/pingcap-inc/clinic/pkg/config"
	"github.com/pingcap-inc/clinic/pkg/db"
	"github.com/pingcap-inc/clinic/pkg/event_bus"
	"github.com/pingcap-inc/clinic/pkg/event_listeners"
	"github.com/pingcap-inc/clinic/pkg/storage/aws"
	"github.com/pingcap-inc/clinic/pkg/storage/iface"
	"github.com/pingcap-inc/clinic/pkg/task_manager"
	"github.com/pingcap-inc/clinic/pkg/upload_api"
	"github.com/pingcap-inc/clinic/pkg/upload_file/decrypt"
	manager2 "github.com/pingcap-inc/clinic/pkg/upload_file/manager"
)

var cfgPath string

func main() {
	flag.StringVar(&cfgPath, "c", "./", "impotered -c=/path/to/config.yaml")
	flag.Parse()

	// no need to load dashboard config
	cfg, err := config.InitConfig(cfgPath)
	if err != nil {
		panic(err)
	}

	err = db.Initialize(cfg.DBConfig, true)
	if err != nil {
		log.Error("init db failed", zap.Error(err))
		panic(err)
	}

	storage, err := newStorage(cfg.FileStorage)
	if err != nil {
		log.Error("init storage failed", zap.Error(err))
		panic(err)
	}

	priv, err := loadPrivateKey(cfg.FileStorage.RSAPrivateKey)
	if err != nil {
		log.Error("load private key failed", zap.Error(err))
		panic(err)
	}

	eksCli, err := aws2.NewEKSCli(cfg.EKSConfig)
	if err != nil {
		log.Error("new eks clii failed", zap.Error(err))
		panic(err)
	}

	taskManagerCli := task_manager.NewTaskManagerClient(cfg.TaskManagerConfig)
	quotaLimiter := resource_limit.NewQuotaLimiter()
	decryptor := decrypt.NewDecrypt(priv, storage, taskManagerCli, quotaLimiter) // split importer
	manager := manager2.NewUploadFileManager(storage)

	event_bus.Register(event_listeners.NewUploadFileEventListener(decryptor, eksCli))

	engine := gin.Default()

	pprof.Register(engine)

	api.InitMetricsApi(engine)

	if err := upload_api.NewAPIService(&upload_api.APIServiceParams{
		Gin:            engine,
		Ctx:            context.Background(),
		Cfg:            cfg,
		Manager:        manager,
		TaskManagerCli: taskManagerCli,
	}).Start(); err != nil {
		log.Fatal("can not start server", zap.Error(err))
	}

	log.Panic("start server failed", zap.Error(engine.Run(cfg.ServerConfig.Addr)))
}

func newStorage(cfg config.FileStorage) (iface.IStorage, error) {
	switch cfg.StorageType() {
	case config.AWSStorageType:
		return aws.NewAWSStorage(cfg)
	default:
		return nil, errors.Errorf("unsupport storage type %s", cfg.StorageType())
	}
}

func loadPrivateKey(kpath string) (*rsa.PrivateKey, error) {
	data, err := os.ReadFile(kpath)
	if err != nil {
		return nil, err
	}
	block, _ := pem.Decode(data)
	if block == nil {
		return nil, fmt.Errorf("decode pem failed")
	}
	priv, err := x509.ParsePKCS1PrivateKey(block.Bytes)
	if err != nil {
		return nil, fmt.Errorf("failed to parse certificate: %s", err.Error())
	}
	return priv, nil
}
