package org

import (
	"fmt"
	"os"
	"testing"
	"time"

	"github.com/pingcap/log"
	"go.uber.org/zap"

	"github.com/pingcap-inc/clinic/pkg/auth/session"
	"github.com/pingcap-inc/clinic/pkg/config"
	"github.com/pingcap-inc/clinic/pkg/datasource"
	"github.com/pingcap-inc/clinic/pkg/db"
	"github.com/pingcap-inc/clinic/pkg/db/model"
	"github.com/pingcap-inc/clinic/pkg/db_docker"
	"github.com/pingcap-inc/clinic/pkg/db_docker/db/impl"
	"github.com/pingcap-inc/clinic/pkg/es"
	"github.com/pingcap-inc/clinic/pkg/rbac"
)

func TestMain(m *testing.M) {
	fmt.Println("start tests")
	impl.ReleaseAll()
	es.StopAllESKibanaDocker()

	_ = db_docker.TryInitDB()
	err := es.StartEsDocker()
	if err != nil {
		log.Error("failed to start es docker", zap.Error(err))
	}
	err = es.StartKibanaDocker()
	if err != nil {
		log.Error("failed to start kibana docker", zap.Error(err))
	}
	initService()
	initData(service.rbac, &service.cfg.SinkConfig.LogConfig)

	code := m.Run()
	es.StopAllESKibanaDocker()
	impl.ReleaseAll()

	os.Exit(code)
}

func initService() {
	service.rbac = initAuth()
	service.cfg = &config.Config{
		SinkConfig: config.SinkConfig{
			LogConfig: config.LogConfig{
				Endpoint: fmt.Sprintf("http://%s:%d", "127.0.0.1", es.EsPort),
				KibanaConfig: config.KibanaConfig{
					AddressConfig: config.AddressConfig{
						Host: "127.0.0.1",
						Port: es.KibanaPort,
					},
				},
				User:     "admin",
				Password: "admin",
			},
		},
	}
	initUser(service.rbac)
}

func initUser(rbac *rbac.AuthCasbin) {
	_, err := model.CreateOrganization("pingcap-org", "create for user attach org", 1, func(orgID uint) error {
		var err error
		if err = rbac.InitOrgRBAC(orgID); err != nil {
			log.Error("apply rbac for org failed", zap.Uint("org", orgID), zap.Error(err))
			return err
		}
		return err
	})
	if err != nil {
		panic(err)
	}

	for i := 1; i < 4; i++ {
		if _, err = model.InsertUserIfAbsent("internal_test",
			fmt.Sprintf("test_10%d_user", i),
			fmt.Sprintf("test0%d", i), true); err != nil {
			panic(err)
		}
	}
	err = service.rbac.AttachRoleForUser(1, 1, 1001)
	if err != nil {
		panic(err)
	}
	err = service.rbac.AttachRoleForUser(1, 2, 1002)
	if err != nil {
		panic(err)
	}
	err = service.rbac.AttachRoleForUser(1, 3, 1003)
	if err != nil {
		panic(err)
	}
}

func initAuth() *rbac.AuthCasbin {
	cfg := &config.Config{
		DBConfig: &config.DBConfig{
			User:     "root", // centralDockerDB.User,
			Host:     db_docker.DockerDB.Host,
			Port:     db_docker.DockerDB.Port,
			Password: db_docker.DockerDB.Password,
			Database: "unittest",
		},
	}
	auth, err := rbac.InitAuth(cfg.DBConfig, "../../../rbac_model")
	if err != nil {
		panic(err)
	}
	err = auth.AttachRoleForUser(1, 1, 1)
	if err != nil {
		panic(err)
	}
	err = auth.AttachRoleForUser(2, 2, 2)
	if err != nil {
		panic(err)
	}
	err = auth.AttachRoleForUser(1, 31, 31)
	if err != nil {
		panic(err)
	}

	return auth
}

func initData(rbac *rbac.AuthCasbin, logconfig *config.LogConfig) {
	_, err := model.CreateOrganization("pingcap-inc", "create for test", 1, func(orgID uint) error {
		var err error
		if err = rbac.InitOrgRBAC(orgID); err != nil {
			log.Error("apply rbac for org failed", zap.Uint("org", orgID), zap.Error(err))
			return err
		}
		return err
	})
	if err != nil {
		panic(err)
	}
	_, err = model.CreateOrganization("pingcap", "create for test", 2, func(orgID uint) error {
		var err error
		if err = rbac.InitOrgRBAC(orgID); err != nil {
			log.Error("apply rbac for org failed", zap.Uint("org", orgID), zap.Error(err))
			return err
		}
		err = es.CreateOrg(orgID, *logconfig)
		if err != nil {
			log.Error("create org in kibana failed", zap.Uint("org", orgID), zap.Error(err))
		}
		return err
	})
	if err != nil {
		panic(err)
	}

	if _, err = datasource.GetOrganization(&session.CtxUser{
		Internal: false,
	}, 1); err != nil {
		panic(err)
	}

	if _, err = model.InsertUserIfAbsent("internal_test", "test_user01", "test01", true); err != nil {
		panic(err)
	}
	if _, err = model.InsertUserIfAbsent("internal_test11", "test_user11", "test01", true); err != nil {
		panic(err)
	}
	for i := 0; i < 10; i++ {
		cid := fmt.Sprintf("Test_01_%d", i)
		name := fmt.Sprintf("test01_%d", i)
		startTime, err := time.Parse("2006-01-02 15:04:05", "2017-04-11 13:0"+fmt.Sprint(i)+":37")
		if err != nil {
			panic(err)
		}
		endTime, err := time.Parse("2006-01-02 15:04:05", "2017-04-12 13:0"+fmt.Sprint(i)+":37")
		if err != nil {
			panic(err)
		}
		_, err = model.AddClusterIfPresent(&db.Cluster{
			ClusterID: cid,
			Name:      name,
			OrgID:     uint(1),
			StartTime: uint64(startTime.Unix()),
			EndTime:   uint64(endTime.Unix()),
		})
		if err != nil {
			panic(err)
		}
	}

	for i := 0; i < 10; i++ {
		cid := fmt.Sprintf("Test_02_%d", i)
		name := fmt.Sprintf("test02_%d", i)
		_, err = model.AddClusterIfPresent(&db.Cluster{
			ClusterID: cid,
			Name:      name,
			OrgID:     uint(2),
		})
		if err != nil {
			panic(err)
		}
	}

	if err != nil {
		panic(err)
	}
}
