package org

import (
	"encoding/json"
	"fmt"
	"net/http"
	"sort"
	"testing"
	"time"

	"github.com/dchest/uniuri"
	"github.com/gin-gonic/gin"
	"github.com/onsi/gomega"
	"github.com/pingcap/log"
	"go.uber.org/zap"
	"gorm.io/gorm"

	"github.com/pingcap-inc/clinic/pkg/api/env"
	"github.com/pingcap-inc/clinic/pkg/auth/api_key"
	"github.com/pingcap-inc/clinic/pkg/auth/utils"
	"github.com/pingcap-inc/clinic/pkg/db"
	"github.com/pingcap-inc/clinic/pkg/db/model"
)

func Test_SortUploadFiles(t *testing.T) {
	sortedByCreateTime := make([]db.UploadFile, 0)

	now := time.Unix(1644854400, 0)
	for i := 0; i < 5; i++ {
		sortedByCreateTime = append(sortedByCreateTime, db.UploadFile{
			Model: gorm.Model{
				CreatedAt: now.Add(time.Duration(i)),
			},
			FileUUID: fmt.Sprintf("sortedByCreateTime%d", i),
		})
	}
	// 0, 1,2,3,4
	sort.Slice(sortedByCreateTime, func(i, j int) bool {
		return sortedByCreateTime[i].CreatedAt.After(sortedByCreateTime[j].CreatedAt)
	})

	sortedByUpdateTime := make([]db.UploadFile, 0)
	for i := 0; i < 4; i++ {
		sortedByUpdateTime = append(sortedByUpdateTime, db.UploadFile{
			Model: gorm.Model{
				UpdatedAt: now.Add(time.Duration(i * 2)),
			},
			FileUUID: fmt.Sprintf("sortedByUpdateTime%d", i*2),
		})
	}
	sort.Slice(sortedByUpdateTime, func(i, j int) bool {
		return sortedByUpdateTime[i].UpdatedAt.After(sortedByUpdateTime[j].UpdatedAt)
	})
	// 0,2,4,6

	sorted := SortUploadFiles(sortedByUpdateTime, sortedByCreateTime, 4)

	g := gomega.NewWithT(t)
	g.Expect(len(sorted)).To(gomega.Equal(4))
	g.Expect(sorted[0].file.FileUUID).To(gomega.Equal("sortedByUpdateTime6"))
	g.Expect(sorted[0].rebuild).To(gomega.BeTrue())
	g.Expect(sorted[1].file.FileUUID).To(gomega.Equal("sortedByCreateTime4"))
	g.Expect(sorted[1].rebuild).To(gomega.BeFalse())
	g.Expect(sorted[2].file.FileUUID).To(gomega.Equal("sortedByUpdateTime4"))
	g.Expect(sorted[2].rebuild).To(gomega.BeTrue())
	g.Expect(sorted[3].file.FileUUID).To(gomega.Equal("sortedByCreateTime3"))
	g.Expect(sorted[3].rebuild).To(gomega.BeFalse())
}

func TestDeleteUser(t *testing.T) {
	g := gomega.NewWithT(t)
	params := []gin.Param{
		{
			Key:   "x-csrf-token",
			Value: "1111",
		},
		{
			Key:   "oid",
			Value: "1",
		},
		{
			Key:   "id",
			Value: "31",
		},
	}

	status := env.EnvironmentInit("DELETE", service.DeleteUser, params, nil, "", service.rbac, &service.cfg.SinkConfig.LogConfig)
	g.Expect(status).To(gomega.Equal(http.StatusOK))
}

func TestListUsers(t *testing.T) {
	// the action(insert role) is triggered at initialization, there is no insert function in progress,so rolename is empty
	g := gomega.NewWithT(t)
	params := []gin.Param{
		{
			Key:   "oid",
			Value: "1",
		},
		{
			Key:   "x-csrf-token",
			Value: "1111",
		},
	}
	status := env.EnvironmentInit("GET", service.ListUsers, params, nil, "", service.rbac, &service.cfg.SinkConfig.LogConfig)
	g.Expect(status).To(gomega.Equal(http.StatusOK))
}

func TestInvite(t *testing.T) {
	g := gomega.NewWithT(t)
	params := []gin.Param{
		{
			Key:   "x-csrf-token",
			Value: "1111",
		},
		{
			Key:   "oid",
			Value: "1",
		},
	}
	reqMap := map[string]any{
		"invites": []map[string]any{
			{
				"role":     1001,
				"userName": "test_user11",
			},
		},
	}
	body, err := json.Marshal(reqMap)
	if err != nil {
		fmt.Println("TestPostReq json.Marshal err:", err)
		return
	}
	status := env.EnvironmentInit("POST", service.Invite, params, body, "", service.rbac, &service.cfg.SinkConfig.LogConfig)
	g.Expect(status).To(gomega.Equal(http.StatusOK))
}

func TestUpdateUser(t *testing.T) {
	g := gomega.NewWithT(t)
	params := []gin.Param{
		{
			Key:   "x-csrf-token",
			Value: "1111",
		},
		{
			Key:   "oid",
			Value: "1",
		},
		{
			Key:   "id",
			Value: "1",
		},
	}
	reqMap := map[string]any{
		"id":   1001,
		"name": "roles01",
	}
	body, err := json.Marshal(reqMap)
	if err != nil {
		fmt.Println("TestPostReq json.Marshal err:", err)
		return
	}
	status := env.EnvironmentInit("PUT", service.UpdateUser, params, body, "", service.rbac, &service.cfg.SinkConfig.LogConfig)
	g.Expect(status).To(gomega.Equal(http.StatusOK))
}

func TestAddCredential(t *testing.T) {
	g := gomega.NewWithT(t)
	params := []gin.Param{
		{
			Key:   "x-csrf-token",
			Value: "1111",
		},
		{
			Key:   "oid",
			Value: "1",
		},
	}

	status := env.EnvironmentInit("POST", service.AddCredential, params, nil, "", service.rbac, &service.cfg.SinkConfig.LogConfig)
	g.Expect(status).To(gomega.Equal(http.StatusOK))
}

func TestListCredentials(t *testing.T) {
	hashedKey, err := utils.EncodePassword(uniuri.New(), fmt.Sprintf("api_key%d", 3))
	if err != nil {
		panic(err)
	}
	_, err = model.AddCredential(1, 1, hashedKey)
	if err != nil {
		log.Error("add credential failed", zap.Uint("user", 1), zap.Uint("org", 1), zap.Error(err))
		panic(err)
	}
	hashedKey, err = utils.EncodePassword(uniuri.New(), fmt.Sprintf("api_key%d", 2))
	if err != nil {
		panic(err)
	}
	_, err = model.AddCredential(1, 1, hashedKey)
	if err != nil {
		log.Error("add credential failed", zap.Uint("user", 1), zap.Uint("org", 1), zap.Error(err))
		panic(err)
	}
	hashedKey, err = utils.EncodePassword(uniuri.New(), fmt.Sprintf("api_key%d", 3))
	if err != nil {
		panic(err)
	}
	_, err = model.AddCredential(1, 1, hashedKey)
	if err != nil {
		log.Error("add credential failed", zap.Uint("user", 3), zap.Uint("org", 1), zap.Error(err))
		panic(err)
	}
	hashedKey, err = utils.EncodePassword(uniuri.New(), fmt.Sprintf("api_key%d", 4))
	if err != nil {
		panic(err)
	}
	_, err = model.AddCredential(1, 1, hashedKey)
	if err != nil {
		log.Error("add credential failed", zap.Uint("user", 4), zap.Uint("org", 1), zap.Error(err))
		panic(err)
	}
	g := gomega.NewWithT(t)
	params := []gin.Param{
		{
			Key:   "x-csrf-token",
			Value: "1111",
		},
		{
			Key:   "oid",
			Value: "1",
		},
	}

	status := env.EnvironmentInit("GET", service.ListCredentials, params, nil, "?limit=3&page=1", service.rbac, &service.cfg.SinkConfig.LogConfig)
	g.Expect(status).To(gomega.Equal(http.StatusOK))
}

func TestDeleteCredential(t *testing.T) {
	jsonKey := api_key.ApiKeyJson{}
	jsonKey.Key = uniuri.New()
	hashedKey, err := utils.EncodePassword(jsonKey.Key, fmt.Sprintf("api_key%d", 1))
	if err != nil {
		panic(err)
	}
	_, err = model.AddCredential(1, 1, hashedKey)
	if err != nil {
		log.Error("add credential failed", zap.Uint("user", 1), zap.Uint("org", 1), zap.Error(err))
		panic(err)
	}
	g := gomega.NewWithT(t)
	params := []gin.Param{
		{
			Key:   "x-csrf-token",
			Value: "1111",
		},
		{
			Key:   "oid",
			Value: "1",
		},
		{
			Key:   "id",
			Value: "1",
		},
	}

	status := env.EnvironmentInit("DELETE", service.DeleteCredential, params, nil, "", service.rbac, &service.cfg.SinkConfig.LogConfig)
	g.Expect(status).To(gomega.Equal(http.StatusOK))
}
