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/dbaas/central/rbac"
	"github.com/pingcap/log"
	"github.com/stretchr/testify/assert"
	"go.uber.org/zap"
	"gorm.io/gorm"
)

func createRole(tenantID entity.ID) *entity.Role {
	role := entity.Role{
		TenantID: tenantID,
		Name:     "unit_test_role",
		Privileges: entity.Privileges{
			Privileges: []entity.Privilege{
				{
					Resource:  rbac.AllResource,
					Operation: rbac.AllOperation,
				},
			},
		},
	}

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

	return &role
}

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

func queryRole(roleID entity.ID) *entity.Role {
	var role entity.Role

	err := db.Transaction(func(tx *gorm.DB) error {
		return tx.Find(&role, "id=?", roleID).Error
	})
	if err != nil {
		log.Info("failed to query role", zap.Error(err))
	}

	return &role
}

func TestAddCentralRole(t *testing.T) {
	url := genURL("/roles")

	// bad request
	form := forms.Role{
		ID:   0,
		Name: "unit test role",
		Privileges: []forms.Privilege{
			{
				Resource:  "",
				Operation: "",
			},
		},
	}
	w := httptest.NewRecorder()

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

	assert.Equal(t, 400, w.Code)

	// create success
	form = forms.Role{
		ID:   0,
		Name: "unit test role",
		Privileges: []forms.Privilege{
			{
				Resource:  rbac.AllResource,
				Operation: rbac.AllOperation,
			},
		},
	}
	w = httptest.NewRecorder()

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

	assert.Equal(t, 200, w.Code)

	// TODO: delete the role?
}

func TestListCentralRoles(t *testing.T) {
	role := createRole(entity.PingCAPTenantID)
	defer deleteRole(role)

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

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

	assert.Equal(t, 200, w.Code)
}

func TestDeleteCentralRole(t *testing.T) {
	role := createRole(entity.PingCAPTenantID)
	defer deleteRole(role)

	url := genURL("/roles/%d", role.ID)
	w := httptest.NewRecorder()

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

	assert.Equalf(t, 200, w.Code, w.Body.String())
	role = queryRole(role.ID)
	assert.Equal(t, 0, int(role.ID.Uint64()))
}

func TestGetCentralRole(t *testing.T) {
	role := createRole(entity.PingCAPTenantID)
	defer deleteRole(role)

	url := genURL("/roles/%d", role.ID)
	w := httptest.NewRecorder()

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

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

func TestUpdateCentralRole(t *testing.T) {
	role := createRole(entity.PingCAPTenantID)
	defer deleteRole(role)

	url := genURL("/roles/%d", role.ID)
	w := httptest.NewRecorder()

	privileges := []forms.Privilege{
		{Resource: rbac.AllResource, Operation: rbac.AllOperation},
	}
	newName := role.Name + "_updated"
	form := forms.Role{
		ID:         0,
		Name:       newName,
		Privileges: privileges,
	}

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

	assert.Equalf(t, 200, w.Code, w.Body.String())
	role = queryRole(role.ID)
	assert.NotNil(t, role)
	assert.Equal(t, newName, role.Name)
}
