package vistrafficmgr

import (
	"fmt"
	"testing"

	log "github.com/InterDigitalInc/AdvantEDGE/go-packages/meep-logger"
)

const ( // FIXME To be update with correct values at the end
	tmName        = "pc"
	tmNamespace   = "postgis-ns"
	tmDBUser      = "postgres"
	tmDBPwd       = "pwd"
	tmDBHost      = "localhost"
	tmDBPort      = "30432"
	tmGridMapFile = "../../test/grid_map.yaml"

	category1              = "poa1-category"
	poaName1               = "poa1-name"
	zeroToThree1           = 0
	threeToSix1            = 0
	sixToNine1             = 0
	nineToTwelve1          = 0
	twelveToFifteen1       = 0
	fifteenToEighteen1     = 0
	eighteenToTwentyOne1   = 0
	twentyOneToTwentyFour1 = 0
	hour1                  = 13
	inRsrp1                = 10
	inRsrq1                = 10
	grid1                  = "(7.422504565000003 43.72723219, 7.422214272000005 43.72747621000001, 7.421491549999999 43.72803665999997, 7.421329629999991 43.72830198999998, 7.421163718000012 43.72867443000003, 7.419276724999997 43.72859253000001, 7.419206675999996 43.72905120999999, 7.418583629000004 43.72901666000001, 7.418475553000015 43.73002454999999, 7.417616122000008 43.72994506000001, 7.417434616999978 43.73032852999999, 7.418496582999996 43.73105429000002, 7.418994657919904 43.73100689871077, 7.419449086174669 43.7308514985659, 7.420256165533474 43.73023629378087, 7.420497749571428 43.72995749603205, 7.420850294302137 43.73005520723608, 7.4215757625905 43.73035387586872, 7.421803319220919 43.7301342399201, 7.422212030034432 43.72965249553761, 7.423253358018433 43.72951012971406, 7.423973082002464 43.72925388225838, 7.42410389544898 43.72788678068092, 7.422504565000003 43.72723219)" // port-de-fontvieille
	expectedGrid1          = "0103000020E61000000100000018000000C7576409A5B01D400153C4F115DD4540F09328F058B01D40B748C1F01DDD454050291B7B9BAF1D40967E264E30DD454033BED60871AF1D40B054E6FF38DD45408B49AC8A45AF1D400A24273445DD45405965B7E056AD1D40DF3A208542DD45404ABDCE8344AD1D409307D08C51DD4540B8C8D42FA1AC1D403878FC6A50DD4540083EF9DA84AC1D4077C3C77171DD4540EB16898FA3AB1D40D549F8D66EDD4540CAEEF0FA73AB1D40C5D7BF677BDD454014E4455E8AAC1D404693DD2F93DD4540D9DB83EF0CAD1D407B8E51A291DD454044D4AD0F84AD1D405C49BA8A8CDD454071A2DBA157AE1D400F12046278DD4540B4CD49F696AE1D40BF764A3F6FDD45409B282A61F3AE1D403EADF37272DD45402873848EB1AF1D4077AE5D3C7CDD4540A6979535EDAF1D40C512ED0975DD454012E7B35958B01D40B0E3C24065DD454005720A5469B11D407E9C829660DD4540F256E6FF25B21D40F063F33058DD4540013DA44A48B21D408E1BDF642BDD4540C7576409A5B01D400153C4F115DD4540"
	area1                  = "poa1-area" // port-de-fontvieille
)

func TestNewTrafficMgr(t *testing.T) {
	fmt.Println("--- ", t.Name())
	log.MeepTextLogInit(t.Name())

	// Invalid Connector
	fmt.Println("Invalid VIS Asset Manager")
	tm, err := NewTrafficMgr("", tmNamespace, tmDBUser, tmDBPwd, tmDBHost, tmDBPort)
	if err == nil || tm != nil {
		t.Fatalf("DB connection should have failed")
	}
	tm, err = NewTrafficMgr(tmName, tmNamespace, tmDBUser, tmDBPwd, "invalid-host", tmDBPort)
	if err == nil || tm != nil {
		t.Fatalf("DB connection should have failed")
	}
	tm, err = NewTrafficMgr(tmName, tmNamespace, tmDBUser, tmDBPwd, tmDBHost, "invalid-port")
	if err == nil || tm != nil {
		t.Fatalf("DB connection should have failed")
	}
	tm, err = NewTrafficMgr(tmName, tmNamespace, tmDBUser, "invalid-pwd", tmDBHost, tmDBPort)
	if err == nil || tm != nil {
		t.Fatalf("DB connection should have failed")
	}

	// Valid Connector
	fmt.Println("Create valid VIS Asset Manager")
	gridMapFilePath = tmGridMapFile
	tm, err = NewTrafficMgr(tmName, tmNamespace, tmDBUser, tmDBPwd, tmDBHost, tmDBPort)
	if err != nil || tm == nil {
		t.Fatalf("Failed to create VIS Asset Manager")
	}

	// Cleanup
	_ = tm.DeleteTables()

	// Create tables
	fmt.Println("Create Tables")
	err = tm.CreateTables()
	if err != nil {
		t.Fatalf("Failed to create table")
	}

	// Cleanup
	err = tm.DeleteTables()
	if err != nil {
		t.Fatalf("Failed to create table")
	}

	// t.Fatalf("DONE")
}

func TestTrafficMgrCreateCategoryTable(t *testing.T) {
	fmt.Println("--- ", t.Name())
	log.MeepTextLogInit(t.Name())

	// Create Connector
	fmt.Println("Create valid VIS Asset Manager")
	gridMapFilePath = tmGridMapFile
	tm, err := NewTrafficMgr(tmName, tmNamespace, tmDBUser, tmDBPwd, tmDBHost, tmDBPort)
	if err != nil || tm == nil {
		t.Fatalf("Failed to create VIS Asset Manager")
	}

	// Cleanup
	_ = tm.DeleteTables()

	// Create tables
	fmt.Println("Create Tables")
	err = tm.CreateTables()
	if err != nil {
		t.Fatalf("Failed to create tables")
	}

	// Make sure Category don't exist
	fmt.Println("Verify no Category present")
	catMap, err := tm.GetAllCategoryLoad()
	if err != nil {
		t.Fatalf("Failed to get all Category")
	}
	if len(catMap) != 0 {
		t.Fatalf("No Category should be present")
	}

	// Add Invalid Category
	fmt.Println("Create Invalid Category")
	catData := map[string]int32{
		FieldZeroToThree:           zeroToThree1,
		FieldThreeToSix:            threeToSix1,
		FieldSixToNine:             sixToNine1,
		FieldNineToTwelve:          nineToTwelve1,
		FieldTwelveToFifteen:       twelveToFifteen1,
		FieldFifteenToEighteen:     fifteenToEighteen1,
		FieldEighteenToTwentyOne:   eighteenToTwentyOne1,
		FieldTwentyOneToTwentyFour: twentyOneToTwentyFour1,
	}
	err = tm.CreateCategoryLoad("", catData) // Invalid category field value
	if err == nil {
		t.Fatalf("Category creation should have failed")
	}

	// Add Category & Validate successfully added
	catData = map[string]int32{
		FieldZeroToThree:           zeroToThree1,
		FieldThreeToSix:            threeToSix1,
		FieldSixToNine:             sixToNine1,
		FieldNineToTwelve:          nineToTwelve1,
		FieldTwelveToFifteen:       twelveToFifteen1,
		FieldFifteenToEighteen:     fifteenToEighteen1,
		FieldEighteenToTwentyOne:   eighteenToTwentyOne1,
		FieldTwentyOneToTwentyFour: twentyOneToTwentyFour1,
	}
	err = tm.CreateCategoryLoad(category1, catData)
	if err != nil {
		t.Fatalf("Failed to create asset: " + err.Error())
	}
	catLoad, err := tm.GetCategoryLoad(category1)
	if err != nil || catLoad == nil {
		t.Fatalf("Failed to get Category")
	}
	// Validate
	if !validateCategory(catLoad, category1, zeroToThree1, threeToSix1, sixToNine1, nineToTwelve1, twelveToFifteen1, fifteenToEighteen1, eighteenToTwentyOne1, twentyOneToTwentyFour1) {
		t.Fatalf("Category validation failed")
	}
	// Delete all & validate updatespoaMap

	fmt.Println("Delete all & validate updates")
	// TODO
	err = tm.DeleteAllCategory()
	if err != nil {
		t.Fatalf("Failed to delete all Category")
	}
	catMap, err = tm.GetAllCategoryLoad()
	if err != nil || len(catMap) != 0 {
		t.Fatalf("Category should no longer exist")
	}

	// t.Fatalf("DONE")
}

func TestTrafficMgrCreateTrafficTable(t *testing.T) {
	fmt.Println("--- ", t.Name())
	log.MeepTextLogInit(t.Name())

	// Create Connector
	fmt.Println("Create valid VIS Asset Manager")
	gridMapFilePath = tmGridMapFile
	tm, err := NewTrafficMgr(tmName, tmNamespace, tmDBUser, tmDBPwd, tmDBHost, tmDBPort)
	if err != nil || tm == nil {
		t.Fatalf("Failed to create VIS Asset Manager")
	}

	// Cleanup
	_ = tm.DeleteTables()

	// Create tables
	fmt.Println("Create Tables")
	err = tm.CreateTables()
	if err != nil {
		t.Fatalf("Failed to create tables")
	}

	// Make sure Traffic don't exist
	fmt.Println("Verify no Traffic present")
	poaLoadMap, err := tm.GetAllPoaLoad()
	if err != nil {
		t.Fatalf("Failed to get all Traffic")
	}
	if len(poaLoadMap) != 0 {
		t.Fatalf("No Traffic should be present")
	}

	// Add Invalid Traffic
	fmt.Println("Create Invalid Traffic")
	err = tm.CreatePoaLoad("", category1) // Invalid poaName field value
	if err == nil {
		t.Fatalf("Traffic creation should have failed")
	}
	err = tm.CreatePoaLoad(poaName1, "") // Invalid category field value
	if err == nil {
		t.Fatalf("Traffic creation should have failed")
	}
	err = tm.CreatePoaLoad(poaName1, category1) // Unknown category field value
	if err == nil {
		t.Fatalf("Traffic creation should have failed due to unknown category field")
	}

	// Add Traffic & Validate successfully added
	// 1. Add Category & Validate successfully added
	catData := map[string]int32{
		FieldZeroToThree:           zeroToThree1,
		FieldThreeToSix:            threeToSix1,
		FieldSixToNine:             sixToNine1,
		FieldNineToTwelve:          nineToTwelve1,
		FieldTwelveToFifteen:       twelveToFifteen1,
		FieldFifteenToEighteen:     fifteenToEighteen1,
		FieldEighteenToTwentyOne:   eighteenToTwentyOne1,
		FieldTwentyOneToTwentyFour: twentyOneToTwentyFour1,
	}
	err = tm.CreateCategoryLoad(category1, catData)
	if err != nil {
		t.Fatalf("Failed to create asset: " + err.Error())
	}
	// 2. Add Traffic
	err = tm.CreatePoaLoad(poaName1, category1)
	if err != nil {
		t.Fatalf("Failed to create asset: " + err.Error())
	}
	// 3. Validate successfully added
	trafficMap, err := tm.GetPoaLoad(poaName1)
	if err != nil || trafficMap == nil {
		t.Fatalf("Failed to get Traffic")
	}
	// Validate
	if !validatePoaLoads(trafficMap, poaName1, category1, zeroToThree1, threeToSix1, sixToNine1, nineToTwelve1, twelveToFifteen1, fifteenToEighteen1, eighteenToTwentyOne1, twentyOneToTwentyFour1) {
		t.Fatalf("Category validation failed")
	}

	// Delete all & validate updatespoaMap
	fmt.Println("Delete all & validate updates")
	err = tm.DeleteAllPoaLoad()
	if err != nil {
		t.Fatalf("Failed to delete all Traffic")
	}
	poaLoadMap, err = tm.GetAllPoaLoad()
	if err != nil || len(poaLoadMap) != 0 {
		t.Fatalf("Traffic should no longer exist")
	}

	// t.Fatalf("DONE")
}

func TestTrafficMgrCreateCreateGridMap(t *testing.T) {
	fmt.Println("--- ", t.Name())
	log.MeepTextLogInit(t.Name())

	// Create Connector
	fmt.Println("Create valid VIS Asset Manager")
	gridMapFilePath = tmGridMapFile
	tm, err := NewTrafficMgr(tmName, tmNamespace, tmDBUser, tmDBPwd, tmDBHost, tmDBPort)
	if err != nil || tm == nil {
		t.Fatalf("Failed to create VIS Asset Manager")
	}

	// Cleanup
	_ = tm.DeleteTables()

	// Create tables
	fmt.Println("Create Tables")
	err = tm.CreateTables()
	if err != nil {
		t.Fatalf("Failed to create tables")
	}

	// Make sure GridMap don't exist
	fmt.Println("Verify no GridMap present")
	gridMaps, err := tm.GetAllGridMap()
	if err != nil {
		t.Fatalf("Failed to get all GridMap")
	}
	if len(gridMaps) != 0 {
		t.Fatalf("No GridMap should be present")
	}

	// Add Invalid GridMap
	fmt.Println("Create Invalid GridMap")
	err = tm.CreateGridMap("", category1, grid1) // Invalid area field value
	if err == nil {
		t.Fatalf("GridMap creation should have failed")
	}
	err = tm.CreateGridMap(area1, "", grid1) // Invalid category field value
	if err == nil {
		t.Fatalf("GridMap creation should have failed")
	}
	err = tm.CreateGridMap(area1, category1, "") // Invalid grid field value
	if err == nil {
		t.Fatalf("GridMap creation should have failed")
	}
	fmt.Println("Invalid checks done")

	// Add Traffic & Validate successfully added
	err = tm.CreateGridMap(area1, category1, grid1)
	if err != nil {
		t.Fatalf("Failed to create asset: " + err.Error())
	}
	gridMap, err := tm.GetGridMap(area1)
	if err != nil || gridMap == nil {
		t.Fatalf("Failed to get GridMap")
	}
	//fmt.Println("Create GridMap: ", gridMap)
	if !validateGridMap(gridMap, area1, category1, expectedGrid1) {
		t.Fatalf("Area validation failed")
	}
	gridMap, err = tm.GetGridMap(area1 + "_unknown")
	if err == nil || gridMap != nil {
		t.Fatalf("GetGridMap should have failed")
	}

	// Delete all & validate updatespoaMap
	fmt.Println("Delete all & validate updates")
	err = tm.DeleteAllGridMap()
	if err != nil {
		t.Fatalf("Failed to delete all GridMap")
	}
	gridMaps, err = tm.GetAllGridMap()
	if err != nil || len(gridMaps) != 0 {
		t.Fatalf("GridMap should no longer exist")
	}

	// t.Fatalf("DONE")
}

func TestPredictQosPerTrafficLoad(t *testing.T) {
	fmt.Println("--- ", t.Name())
	log.MeepTextLogInit(t.Name())

	// Create Connector
	fmt.Println("Create valid VIS Asset Manager")
	gridMapFilePath = tmGridMapFile
	tm, _ := NewTrafficMgr(tmName, tmNamespace, tmDBUser, tmDBPwd, tmDBHost, tmDBPort)

	// Cleanup
	_ = tm.DeleteTables()

	// Create tables
	fmt.Println("Create Tables")
	_ = tm.CreateTables()

	// Add Traffic & Validate successfully added
	catData := map[string]int32{
		FieldZeroToThree:           zeroToThree1,
		FieldThreeToSix:            threeToSix1,
		FieldSixToNine:             sixToNine1,
		FieldNineToTwelve:          nineToTwelve1,
		FieldTwelveToFifteen:       twelveToFifteen1,
		FieldFifteenToEighteen:     fifteenToEighteen1,
		FieldEighteenToTwentyOne:   eighteenToTwentyOne1,
		FieldTwentyOneToTwentyFour: twentyOneToTwentyFour1,
	}
	_ = tm.CreateCategoryLoad(category1, catData)
	_ = tm.CreatePoaLoad(poaName1, category1)
	fmt.Println("Tables initialized")

	// Invalid hour
	_, _, err := tm.PredictQosPerTrafficLoad(25, 0, 0, poaName1)
	if err == nil {
		t.Fatalf("Should have failed due to invalid hour")
	}
	// Invalid poaName
	_, _, err = tm.PredictQosPerTrafficLoad(14, 0, 0, "")
	if err == nil {
		t.Fatalf("Should have failed due to invalid poaName")
	}
	// FIXME Are there any invalid Rsrp/Rsrq values?
	/*_, _, err = tm.PredictQosPerTrafficLoad(14, -1, 0, poaName1)
	if err == nil {
		t.Fatalf("Should have failed due to invalid inRsrp")
	}
	// Invalid inRsrq
	_, _, err = tm.PredictQosPerTrafficLoad(14, 0, -1, poaName1)
	if err == nil {
		t.Fatalf("Should have failed due to invalid inRsrq")
	}*/
	fmt.Println("Invalid checks done")

	// FIXME Execute the test with proper values for inRsrp1 and inRsrq1
	rsrp, rsrq, err := tm.PredictQosPerTrafficLoad(hour1, inRsrp1, inRsrq1, poaName1)
	if err != nil {
		t.Fatal("Failed to get predicted Qos per Traffic load:", err.Error())
	}
	// Validate
	if !validatePredictQosPerTrafficLoad(rsrp, rsrq, inRsrp1, inRsrq1) {
		t.Fatalf("Category validation failed")
	}

	// Delete all & validate updatespoaMap
	fmt.Println("Delete all & validate updates")
	_ = tm.DeleteAllPoaLoad()
	_, _ = tm.GetAllPoaLoad()

	// t.Fatalf("DONE")
}

func validateCategory(categoryLoads *CategoryLoads, category string, zeroToThree int32, threeToSix int32, sixToNine int32, nineToTwelve int32, twelveToFifteen int32, fifteenToEighteen int32, eighteenToTwentyOne int32, twentyOneToTwentyFour int32) bool {
	if categoryLoads == nil {
		fmt.Println("categoryLoads == nil")
		return false
	}
	if categoryLoads.Category != category {
		fmt.Println("CategoryLoads.Category != category")
		return false
	}
	if categoryLoads.ZeroToThree != zeroToThree {
		fmt.Println("CategoryLoads.ZeroToThree != zeroToThree")
		return false
	}
	if categoryLoads.ThreeToSix != threeToSix {
		fmt.Println("CategoryLoads.ThreeToSix != threeToSix")
		return false
	}
	if categoryLoads.SixToNine != sixToNine {
		fmt.Println("CategoryLoads.SixToNine != sixToNine")
		return false
	}
	if categoryLoads.NineToTwelve != nineToTwelve {
		fmt.Println("CategoryLoads.NineToTwelve != nineToTwelve")
		return false
	}
	if categoryLoads.EighteenToTwentyOne != eighteenToTwentyOne {
		fmt.Println("CategoryLoads.EighteenToTwentyOne != eighteenToTwentyOne")
		return false
	}
	if categoryLoads.FifteenToEighteen != fifteenToEighteen {
		fmt.Println("CategoryLoads.FifteenToEighteen != fifteenToEighteen")
		return false
	}
	if categoryLoads.FifteenToEighteen != fifteenToEighteen {
		fmt.Println("CategoryLoads.FifteenToEighteen != fifteenToEighteen")
		return false
	}
	if categoryLoads.TwentyOneToTwentyFour != twentyOneToTwentyFour {
		fmt.Println("CategoryLoads.TwentyOneToTwentyFour != twentyOneToTwentyFour")
		return false
	}

	return true
}

func validatePoaLoads(poaLoads *PoaLoads, poaName string, category string, zeroToThree int32, threeToSix int32, sixToNine int32, nineToTwelve int32, twelveToFifteen int32, fifteenToEighteen int32, eighteenToTwentyOne int32, twentyOneToTwentyFour int32) bool {
	if poaLoads == nil {
		fmt.Println("poaLoads == nil")
		return false
	}
	if poaLoads.PoaName != poaName {
		fmt.Println("PoaLoads.PoaName != poaName")
		return false
	}
	if poaLoads.Category != category {
		fmt.Println("PoaLoads.Category != category")
		return false
	}
	if poaLoads.ZeroToThree != zeroToThree {
		fmt.Println("PoaLoads.ZeroToThree != zeroToThree")
		return false
	}
	if poaLoads.ThreeToSix != threeToSix {
		fmt.Println("PoaLoads.ThreeToSix != threeToSix")
		return false
	}
	if poaLoads.SixToNine != sixToNine {
		fmt.Println("PoaLoads.SixToNine != sixToNine")
		return false
	}
	if poaLoads.NineToTwelve != nineToTwelve {
		fmt.Println("PoaLoads.NineToTwelve != nineToTwelve")
		return false
	}
	if poaLoads.EighteenToTwentyOne != eighteenToTwentyOne {
		fmt.Println("PoaLoads.EighteenToTwentyOne != eighteenToTwentyOne")
		return false
	}
	if poaLoads.FifteenToEighteen != fifteenToEighteen {
		fmt.Println("PoaLoads.FifteenToEighteen != fifteenToEighteen")
		return false
	}
	if poaLoads.FifteenToEighteen != fifteenToEighteen {
		fmt.Println("PoaLoads.FifteenToEighteen != fifteenToEighteen")
		return false
	}
	if poaLoads.TwentyOneToTwentyFour != twentyOneToTwentyFour {
		fmt.Println("PoaLoads.TwentyOneToTwentyFour != twentyOneToTwentyFour")
		return false
	}

	return true
}

func validatePredictQosPerTrafficLoad(rsrp int32, rsrq int32, expectedRsrp int32, expectedRsrq int32) bool {
	if rsrp != expectedRsrp {
		fmt.Println("rsrp != expectedRsrp")
		return false
	}
	if rsrq != expectedRsrq {
		fmt.Println("rsrq != expectedRsrq")
		return false
	}
	return true
}

func validateGridMap(gridMap *GridMapTable, area string, category string, grid string) bool {
	if gridMap == nil {
		fmt.Println("gridMap == nil")
		return false
	}
	if gridMap.area != area {
		fmt.Println("gridMap.area != area")
		return false
	}
	if gridMap.category != category {
		fmt.Println("gridMap.category != category")
		return false
	}
	if gridMap.grid != grid {
		fmt.Println("gridMap.grid != grid")
		return false
	}
	return true
}
