package api

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

	"github.com/pingcap/dbaas/admin/forms"
	"github.com/pingcap/dbaas/central/db"
	"github.com/pingcap/dbaas/central/db/entity"
	"github.com/pingcap/log"
	"github.com/stretchr/testify/assert"
	"go.uber.org/zap"
	"gorm.io/gorm"
)

func createProvider() *entity.Provider {
	provider := entity.Provider{
		Name:   entity.Cloud("aws"),
		Status: entity.ProviderStatusActive,
	}

	if err := db.Create(&provider); err != nil {
		log.Info("failed to create provider", zap.Error(err))
	}

	return &provider
}

func deleteProvider(provider *entity.Provider) {
	if err := db.Delete(provider, "id = ?", provider.ID); err != nil {
		log.Info("failed to delete provider", zap.Error(err))
	}
}

func queryProviderByName(name string) *entity.Provider {
	var provider entity.Provider

	err := db.Transaction(func(tx *gorm.DB) error {
		return tx.First(&provider, "name = ?", name).Error
	})
	if err != nil {
		log.Error("failed to query provider by name", zap.String("name", name))
	}

	return &provider
}

func createRegion(providerID entity.ID) *entity.Region {
	zones := []*entity.Zone{
		{
			Name:        "zone1",
			DisplayName: "zone1",
		},
		{
			Name:        "zone1",
			DisplayName: "zone1",
		},
		{
			Name:        "zone1",
			DisplayName: "zone1",
		},
	}

	region := entity.Region{
		Name:        "unit test region",
		DisplayName: "UnitTest Region",
		ProviderID:  providerID,
		Zones:       zones,
		Status:      entity.RegionStatusActive,
	}

	if err := db.Create(&region); err != nil {
		log.Info("failed to create region", zap.Error(err))
	}

	return &region
}

func deleteRegion(region *entity.Region) {
	if err := db.Delete(region, "id = ?", region.ID); err != nil {
		log.Info("failed to delete region", zap.Error(err))
	}
}

func TestAddProvider(t *testing.T) {
	// delete existing aws provider
	provider := queryProviderByName("aws")
	deleteProvider(provider)

	form := forms.Provider{
		Name:   "aws",
		Status: string(entity.ProviderStatusActive),
	}

	url := genURL("/providers")
	w := httptest.NewRecorder()

	req, _ := http.NewRequest("POST", url, encodeJSON(&form))
	router.ServeHTTP(w, req)

	assert.Equalf(t, 200, w.Code, w.Body.String())

	// delete it
	provider = queryProviderByName("aws")
	deleteProvider(provider)
}

func TestListProviders(t *testing.T) {
	provider := createProvider()
	defer deleteProvider(provider)

	url := genURL("/providers")
	w := httptest.NewRecorder()

	req, _ := http.NewRequest("GET", url, nil)
	router.ServeHTTP(w, req)

	assert.Equalf(t, 200, w.Code, w.Body.String())
}

func TestAddRegion(t *testing.T) {
	provider := createProvider()
	defer deleteProvider(provider)

	// test add
	form := forms.ProviderRegion{
		Name:              "us-west",
		DisplayName:       "AWS US WEST",
		Status:            string(entity.ProviderStatusActive),
		Provider:          "aws",
		Zones:             []string{"us-west-1", "us-west-2", "us-west-3"},
		IsPublic:          true,
		OpenedOrgIDs:      nil,
		DefaultBackupTime: "15:04",
	}

	url := genURL("/providers/%s/regions", provider.Name)
	w := httptest.NewRecorder()

	req, _ := http.NewRequest("POST", url, encodeJSON(&form))
	router.ServeHTTP(w, req)

	assert.Equalf(t, 200, w.Code, w.Body.String())
}

func TestUpdateRegion(t *testing.T) {
	provider := createProvider()
	defer deleteProvider(provider)
	region := createRegion(provider.ID)
	defer deleteRegion(region)

	jsonRegion := forms.ProviderRegion{
		Name:              region.Name + "_HAHA",
		DisplayName:       region.DisplayName,
		Status:            string(region.Status),
		Provider:          "aws",
		Zones:             []string{"abc1", "abc2", "abc3"},
		IsPublic:          true,
		OpenedOrgIDs:      nil,
		DefaultBackupTime: "15:04",
	}
	url := genURL("/providers/%s/regions/%d", provider.Name, region.ID)

	w := httptest.NewRecorder()
	req, _ := http.NewRequest("PUT", url, encodeJSON(&jsonRegion))
	router.ServeHTTP(w, req)
	result := forms.ProviderRegion{}
	decodeJSON(w.Body.Bytes(), &result)
	assert.Equalf(t, 200, w.Code, w.Body.String())
	assert.Containsf(t, result.Name, "_HAHA", w.Body.String())
}

func TestListRegions(t *testing.T) {
	provider := createProvider()
	defer deleteProvider(provider)
	region := createRegion(provider.ID)
	defer deleteRegion(region)

	url := genURL("/providers/%s/regions", provider.Name)

	w := httptest.NewRecorder()
	req, _ := http.NewRequest("GET", url, nil)
	router.ServeHTTP(w, req)
	assert.Equalf(t, 200, w.Code, w.Body.String())
}

func TestDeleteRegion(t *testing.T) {
	provider := createProvider()
	defer deleteProvider(provider)
	region := createRegion(provider.ID)
	defer deleteRegion(region)

	url := genURL("/providers/%s/regions/%d", provider.Name, region.ID)
	w := httptest.NewRecorder()
	req, _ := http.NewRequest("DELETE", url, nil)
	router.ServeHTTP(w, req)
	assert.Equalf(t, 404, w.Code, w.Body.String())

	//region = queryRegion(region.ID)
	//assert.Equalf(t, uint64(0), region.ID.Uint64(), region.Name)
}

func TestGetRegionByID(t *testing.T) {
	provider := createProvider()
	defer deleteProvider(provider)
	region := createRegion(provider.ID)
	defer deleteRegion(region)

	url := genURL("/providers/%s/regions/%d", provider.Name, region.ID)
	w := httptest.NewRecorder()
	req, _ := http.NewRequest("GET", url, nil)
	router.ServeHTTP(w, req)
	assert.Equalf(t, 200, w.Code, w.Body.String())
}
