package iam

import (
	"fmt"
	"net/http"

	l4g "github.com/alecthomas/log4go"
	"github.com/stretchr/testify/assert"
	"github.com/tidbcloud/qa-PlatformAPIAuto/common"
	"github.com/tidbcloud/qa-PlatformAPIAuto/models"
	"github.com/tidbcloud/qa-gourd/ProductModel"
	"github.com/tidbcloud/qa-gourd/ProductService"
	"github.com/tidbcloud/qa-gourd/util"
)

func RunUserProfileTest(w *ProductService.WebRequestInfo, UrlInfoMap map[string]ProductService.UrlInfo, reporter *util.AllureReporter) {
	at := &AccountTest{}
	at.Init(w, UrlInfoMap, reporter)
	at.CheckUserProfileScene()
}

func (at *AccountTest) CheckUserProfileScene() {
	// get Original org/user info
	at.Reporter.StepReport("A-GetUserProfileOriginalInfo", func() {
		at.SetDBaaSUserInfo(at.w.User.TenantEmail)
		at.SetDBaaSUserId()
		at.SetDBaaSUserProfileV2()
	})

	// update user profile settings
	at.Reporter.StepReport("B-UpdateUserInfo", func() {
		at.UpdateUserInfo(UserTestFirstName, UserTestLastName, UserTestPhone)
	})

	// update time zone settings
	at.Reporter.StepReport("C-UpdateTimezoneSettings", func() {
		at.UpdateOrgInfo(OrgTestTimeZone, OrgTestName, OrgTestUsetSetTimeZone)
	})

	// Get user current info
	var UserProfile *ProductModel.UserProfileV2
	var err error
	at.Reporter.StepReport("D-GetUserCurrentProfile", func() {
		UserProfile, err = at.GetUserProfileV2()
		if err != nil {
			l4g.Info("GetUserProfileV2 error : %v", err)
		}
	})

	at.Reporter.StepReport("E-CheckOrgInfo", func() {
		OrgInfo, err := common.GetOrgInfoFromUserProfileV2(UserProfile, at.w.User.OrgId)
		if err != nil {
			l4g.Info("GetOrgInfoFromUserProfileV2 error : %v", err)
		}
		OrgList, err := at.GetOrgsV1()
		if err != nil {
			l4g.Info("GetOrgsV1 error : %v", err)
		}
		OrgTestType := common.GetOrgInfoDetailFromOrgsV1(OrgList, at.w.User.OrgId, "OrgType")
		at.Reporter.StepReport(fmt.Sprintf(`check UserInfo, Expect First Name is : [%s] , Expect Last Name is : [%s] , Expect Phone is : [%s] ; 
		Actual First Name is : [%s] , Actual Last Name is : [%s] , Actual Phone is : [%s]`,
			UserTestFirstName, UserTestLastName, UserTestPhone,
			UserProfile.FirstName, UserProfile.LastName, UserProfile.PhoneNumber), func() {
			assert.Equal(at.Reporter.T, UserTestFirstName, UserProfile.FirstName)
			assert.Equal(at.Reporter.T, UserTestLastName, UserProfile.LastName)
			assert.Equal(at.Reporter.T, UserTestPhone, UserProfile.PhoneNumber)
		})
		at.Reporter.StepReport(fmt.Sprintf(`check OrgInfo, Expect OrgName is : [%s] , Expect OrgType is : [%s] , Expect TimeZone is : [%v] , Expect UserSetTimeZone is : [%s] ; 
		Actual OrgName is : [%s] , Actual OrgType is : [%s] , Actual TimeZone is : [%v] , Actual UserSetTimeZone is : [%s]`,
			OrgTestName, OrgTestType, OrgTestTimeZone, OrgTestUsetSetTimeZone,
			OrgInfo.Name, OrgInfo.OrgType, OrgInfo.Timezone, OrgInfo.UserSetTimezone), func() {
			assert.Equal(at.Reporter.T, OrgTestName, OrgInfo.Name)
			assert.Equal(at.Reporter.T, OrgTestType, OrgInfo.OrgType)
			assert.Equal(at.Reporter.T, OrgTestTimeZone, OrgInfo.Timezone)
			//TODO: remove check of timezone and add check for user set timezone
			// assert.Equal(at.Reporter.T, OrgTestUsetSetTimeZone, OrgInfo.UserSetTimezone)
		})

	})

	// check project info
	// Get user profile & org info
	at.Reporter.StepReport("F-CheckProjectInfo", func() {
		UserProfileProjects, _ := common.GetProjectsFromUserProfileV2(UserProfile, at.w.User.OrgId)
		Projects, err := at.GetAllProjects()
		if err != nil {
			l4g.Info("GetAllProjects error : %v", err)
		}
		at.CrossCheckProjects(UserProfileProjects, Projects)
	})

	// check user id
	at.Reporter.StepReport("G-CheckUserId", func() {
		at.Reporter.StepReport(fmt.Sprintf(`check UserInfo, Expect UserId is : [%s] , Actual UserId is : [%s]`,
			at.DBaaSUserInfo.UserId, UserProfile.UserId), func() {
			assert.Equal(at.Reporter.T, at.DBaaSUserInfo.UserId, UserProfile.UserId)
		})
	})

	// recover previous user profile & org info
	at.Reporter.StepReport("H-RecoverUserOrgInfo", func() {
		at.UpdateUserInfo(at.DBaaSUserInfo.UserFirstName, at.DBaaSUserInfo.UserLastName, at.DBaaSUserInfo.UserPhone)
		at.UpdateOrgInfo(at.OrgSettings.TimeZone, at.OrgSettings.OrgName, at.OrgSettings.UserSetTimeZone)
	})

}

func (at *AccountTest) UpdateOrgInfo(TimeZone int32, OrgName string, UserSetTimezone string) {
	pathParam := map[string]string{
		"org_id": at.w.User.OrgId,
	}
	body := map[string]interface{}{
		"name":              OrgName,
		"timezone":          TimeZone,
		"user_set_timezone": UserSetTimezone,
	}
	UpdateOrgInfoResp := at.w.DoWebAPIRequestByUrlInfo(at.URLInfoMap["setOrgInfo"], body, pathParam, nil)
	at.Reporter.StepReport(fmt.Sprintf("UpdateOrgInfo, http status code is : %v (should be %v)",
		UpdateOrgInfoResp.Raw().StatusCode,
		http.StatusOK,
	),
		func() {
			assert.Equal(at.Reporter.T, http.StatusOK, UpdateOrgInfoResp.Raw().StatusCode)
		})
}

func (at *AccountTest) UpdateUserInfo(first_name, last_name, phone_number string) {
	pathParam := map[string]string{
		"org_id": at.w.User.OrgId,
	}
	body := map[string]interface{}{
		"first_name":   first_name,
		"last_name":    last_name,
		"org_id":       at.w.User.OrgId,
		"phone_number": phone_number,
	}
	putUserInfoResp := at.w.DoWebAPIRequestByUrlInfo(at.URLInfoMap["putUserInfo"], body, pathParam, nil)
	at.Reporter.StepReport(fmt.Sprintf("putUserInfo, http status code is : %v (should be %v)",
		putUserInfoResp.Raw().StatusCode,
		http.StatusOK,
	),
		func() {
			assert.Equal(at.Reporter.T, http.StatusOK, putUserInfoResp.Raw().StatusCode)
		})
}

func (at *AccountTest) CrossCheckProjects(UserProfileProjects *[]ProductModel.UserProfileProject, Projects *models.CentralProjects) {
	UserProfileProjectIdMap := make(map[string]bool)
	result := true
	for _, UserProfileProject := range *UserProfileProjects {
		if _, ok := UserProfileProjectIdMap[UserProfileProject.Id]; ok {
			result = assert.Fail(at.Reporter.T, "UserProfileProjectIdMap has duplicate key, UserProfilev2 returns duplicate project id")
		} else {
			UserProfileProjectIdMap[UserProfileProject.Id] = false
		}
	}
	for _, Project := range Projects.Items {
		if _, ok := UserProfileProjectIdMap[*Project.ID]; !ok {
			result = assert.Fail(at.Reporter.T, "UserProfilev2 doesn't return project id : %s", *Project.ID)
		} else {
			UserProfileProjectIdMap[*Project.ID] = true
		}
	}
	for Id, value := range UserProfileProjectIdMap {
		if !value {
			result = assert.Fail(at.Reporter.T, "UserProfilev2 returns project id : %s , but Projects doesn't have this project", Id)
		}
	}
	at.Reporter.StepReport(fmt.Sprintf(`CrossCheckProjects , UserProfileProjects : %+v , UserProfileProjectIdMap : %+v`,
		*UserProfileProjects,
		UserProfileProjectIdMap,
	),
		func() {
			assert.Equal(at.Reporter.T, true, result)
		})
}

func (at *AccountTest) Get(TimeZone int, OrgName string) {
	pathParam := map[string]string{
		"org_id": at.w.User.OrgId,
	}
	body := map[string]interface{}{
		"name":     OrgName,
		"timezone": TimeZone,
	}
	UpdateOrgInfoResp := at.w.DoWebAPIRequestByUrlInfo(at.URLInfoMap["setOrgInfo"], body, pathParam, nil)
	at.Reporter.StepReport(fmt.Sprintf("UpdateOrgInfo, http status code is : %v (should be %v)",
		UpdateOrgInfoResp.Raw().StatusCode,
		http.StatusOK,
	),
		func() {
			assert.Equal(at.Reporter.T, http.StatusOK, UpdateOrgInfoResp.Raw().StatusCode)
		})
}
