package server

import (
	"net/http"
	"net/http/httptest"
	"testing"

	_ "github.com/go-sql-driver/mysql"
	"github.com/golang/mock/gomock"
	"github.com/stretchr/testify/assert"

	"github.com/baetyl/baetyl-cloud/v2/api"
	"github.com/baetyl/baetyl-cloud/v2/common"
	"github.com/baetyl/baetyl-cloud/v2/config"
	mockPlugin "github.com/baetyl/baetyl-cloud/v2/mock/plugin"
	"github.com/baetyl/baetyl-cloud/v2/models"
	"github.com/baetyl/baetyl-cloud/v2/plugin"
)

func initMisServerMock(t *testing.T) (*MisServer, *gomock.Controller) {
	c := &config.CloudConfig{}
	c.Plugin.Auth = common.RandString(9)
	c.Plugin.Sign = common.RandString(9)
	c.Plugin.Resource = common.RandString(9)
	c.Plugin.Shadow = common.RandString(9)
	c.Plugin.Index = common.RandString(9)
	c.Plugin.AppHistory = common.RandString(9)
	c.Plugin.Objects = []string{common.RandString(9)}
	c.Plugin.PKI = common.RandString(9)
	c.Plugin.Functions = []string{common.RandString(9)}
	c.Plugin.License = common.RandString(9)
	c.Plugin.Quota = common.RandString(9)
	c.Plugin.Property = common.RandString(9)
	c.Plugin.Module = common.RandString(9)
	c.Plugin.Task = common.RandString(9)
	c.Plugin.Locker = common.RandString(9)
	c.Plugin.Tx = common.RandString(9)
	c.Plugin.Cron = common.RandString(9)
	c.Plugin.Cache = common.RandString(9)
	mockCtl := gomock.NewController(t)

	mockObjectStorage := mockPlugin.NewMockObject(mockCtl)
	for _, v := range c.Plugin.Objects {
		plugin.RegisterFactory(v, func() (plugin.Plugin, error) {
			return mockObjectStorage, nil
		})
	}
	mockFunction := mockPlugin.NewMockFunction(mockCtl)
	for _, v := range c.Plugin.Functions {
		plugin.RegisterFactory(v, func() (plugin.Plugin, error) {
			return mockFunction, nil
		})
	}
	mockAuth := mockPlugin.NewMockAuth(mockCtl)
	plugin.RegisterFactory(c.Plugin.Auth, func() (plugin.Plugin, error) {
		return mockAuth, nil
	})

	mockSign := mockPlugin.NewMockSign(mockCtl)
	plugin.RegisterFactory(c.Plugin.Sign, func() (plugin.Plugin, error) {
		return mockSign, nil
	})

	mPKI := mockPlugin.NewMockPKI(mockCtl)
	plugin.RegisterFactory(c.Plugin.PKI, func() (plugin.Plugin, error) {
		return mPKI, nil
	})

	mLicense := mockPlugin.NewMockLicense(mockCtl)
	plugin.RegisterFactory(c.Plugin.License, func() (plugin.Plugin, error) {
		return mLicense, nil
	})
	mQuota := mockPlugin.NewMockQuota(mockCtl)
	plugin.RegisterFactory(c.Plugin.Quota, func() (plugin.Plugin, error) {
		return mQuota, nil
	})
	mockProperty := mockPlugin.NewMockProperty(mockCtl)
	plugin.RegisterFactory(c.Plugin.Property, func() (plugin.Plugin, error) {
		return mockProperty, nil
	})
	mockModule := mockPlugin.NewMockModule(mockCtl)
	plugin.RegisterFactory(c.Plugin.Module, func() (plugin.Plugin, error) {
		return mockModule, nil
	})
	mockResource := mockPlugin.NewMockResource(mockCtl)
	plugin.RegisterFactory(c.Plugin.Resource, func() (plugin.Plugin, error) {
		return mockResource, nil
	})
	mockShadow := mockPlugin.NewMockShadow(mockCtl)
	plugin.RegisterFactory(c.Plugin.Shadow, func() (plugin.Plugin, error) {
		return mockShadow, nil
	})
	plugin.RegisterFactory(c.Plugin.Resource, func() (plugin.Plugin, error) {
		return mockResource, nil
	})
	plugin.RegisterFactory(c.Plugin.Resource, func() (plugin.Plugin, error) {
		return mockResource, nil
	})
	res := &models.ConfigurationList{}
	mockResource.EXPECT().ListConfig(gomock.Any(), gomock.Any()).Return(res, nil).AnyTimes()

	plugin.RegisterFactory(c.Plugin.Resource, func() (plugin.Plugin, error) {
		return mockResource, nil
	})

	plugin.RegisterFactory(c.Plugin.Resource, func() (plugin.Plugin, error) {
		return mockResource, nil
	})

	mockIndex := mockPlugin.NewMockIndex(mockCtl)
	plugin.RegisterFactory(c.Plugin.Index, func() (plugin.Plugin, error) {
		return mockIndex, nil
	})

	mockTask := mockPlugin.NewMockTask(mockCtl)
	plugin.RegisterFactory(c.Plugin.Task, func() (plugin.Plugin, error) {
		return mockTask, nil
	})

	mockLocker := mockPlugin.NewMockLocker(mockCtl)
	plugin.RegisterFactory(c.Plugin.Locker, func() (plugin.Plugin, error) {
		return mockLocker, nil
	})

	mockTx := mockPlugin.NewMockTransactionFactory(mockCtl)
	plugin.RegisterFactory(c.Plugin.Tx, func() (plugin.Plugin, error) {
		return mockTx, nil
	})
	mockCronApp := mockPlugin.NewMockCron(mockCtl)
	plugin.RegisterFactory(c.Plugin.Cron, func() (plugin.Plugin, error) {
		return mockCronApp, nil
	})

	mockCache := mockPlugin.NewMockDataCache(mockCtl)
	plugin.RegisterFactory(c.Plugin.Cache, func() (plugin.Plugin, error) {
		return mockCache, nil
	})
	mockAPI, err := api.NewAPI(c)
	assert.NoError(t, err)

	s, err := NewMisServer(c)
	assert.NoError(t, err)
	s.SetAPI(mockAPI)

	return s, mockCtl
}

func TestMisServer_Handler(t *testing.T) {
	s, mockCtl := initMisServerMock(t)
	defer mockCtl.Finish()

	s.InitRoute()
	s.router.Use(s.authHandler)
	r := s.GetRoute()
	assert.NotNil(t, r)
	// https 200
	req, _ := http.NewRequest(http.MethodGet, "/health", nil)
	req.Header.Set("baetyl-cloud-token", "baetyl-cloud-token")
	req.Header.Set("baetyl-cloud-user", "1")
	w := httptest.NewRecorder()
	s.GetRoute().ServeHTTP(w, req)
	assert.Equal(t, http.StatusOK, w.Code)
	go s.Run()
	defer s.Close()
	// http 200
	req, _ = http.NewRequest(http.MethodGet, "/health", nil)
	req.Header.Set("baetyl-cloud-token", "baetyl-cloud-token")
	req.Header.Set("baetyl-cloud-user", "")
	w = httptest.NewRecorder()
	s.GetRoute().ServeHTTP(w, req)
	assert.Equal(t, http.StatusOK, w.Code)
	go s.Run()
	defer s.Close()
	// http 200
	req, _ = http.NewRequest(http.MethodGet, "/health", nil)
	w = httptest.NewRecorder()
	s.GetRoute().ServeHTTP(w, req)
	assert.Equal(t, http.StatusOK, w.Code)
	go s.Run()
	defer s.Close()
}
