package controllers

import (
	"encoding/json"
	"fmt"
	"strconv"
	"time"

	"SSO/enums"
	"SSO/models"
	"SSO/service"
	"SSO/utils"
)

type VerifyTokenController struct {
	BaseController
}

func (this *VerifyTokenController) Prepare() {
	this.EnableXSRF = false
}

// /verifytoken [post]
// @param token:string
// @param app_type:int
// @param ip:string
// @param session_id:string 客户端（浏览器）sessionID
// return 说明：所有数值型的 key 和 value 都转换为了 string 类型
func (this *VerifyTokenController) VerifyToken() {
	token := this.GetString("token")
	clientSessionId := this.GetString("session_id")
	app_type, _ := this.GetInt("app_type", 0)
	// if app_type != 1 && app_type != 3 && app_type != 5 {
	// 	this.Data["json"] = enums.MakeFailResponseJSONWithSGJErrorCode(enums.ErrorCodeParamWrong)
	// 	this.ServeJSON()
	// 	return
	// }
	if url := service.GetAppURLWithAppType(app_type); len(url) == 0 {
		this.Data["json"] = enums.MakeFailResponseJSONWithSGJErrorCode(enums.ErrorCodeParamWrong)
		this.ServeJSON()
		return
	}
	utils.TraceLog("clientSessionId: %v", clientSessionId)
	if len(clientSessionId) == 0 {
		this.Data["json"] = enums.MakeFailResponseJSONWithSGJErrorCode(enums.ErrorCodeParamWrong)
		this.ServeJSON()
		return
	}

	if len(token) <= 24 {
		this.Data["json"] = enums.MakeFailResponseJSONWithSGJErrorCode(enums.ErrorCodeInvalidToken)
		this.ServeJSON()
		return
	}
	redisClient := service.RedisClient()
	defer redisClient.Close()
	mobile := token[24:]
	// locToken, _ := redisClient.Get(fmt.Sprintf("sso_token_%v", mobile)).Result()
	locToken, _ := redisClient.Get(fmt.Sprintf("sso_token_%v", clientSessionId)).Result()
	utils.TraceLog("request token: %v \t server cached token: %v", token, locToken)
	var isUserInfoFromRedis bool = false
	if token == locToken {
		infoStr, getCachedInfoErr := redisClient.Get("sso_admin_user_info_" + mobile).Result()
		if getCachedInfoErr != nil {
			utils.WarningLog("redis 获取管理员用户信息失败：%v", getCachedInfoErr)
			infoStr = ""
		}
		infoStr = ""
		var userInfo map[string]interface{}
		if len(infoStr) == 0 {
			utils.TraceLog("%v data is from operation", "sso_admin_user_info_"+mobile)
			isUserInfoFromRedis = false
			info, getInfoErr := this.GetAdminUserAllInfo(mobile)
			if getInfoErr != nil {
				this.Data["json"] = enums.MakeFailResponseJSONWithSGJError(getInfoErr)
				this.ServeJSON()
				return
			}
			userInfo = info
			infoStr_b, _ := json.Marshal(userInfo)
			redisClient.Set("sso_admin_user_info_"+mobile, string(infoStr_b), time.Duration(2)*time.Minute)

		} else {
			utils.TraceLog("%v data is from redis cache", "sso_admin_user_info_"+mobile)
			isUserInfoFromRedis = true
			userInfo = make(map[string]interface{})
			json.Unmarshal([]byte(infoStr), &userInfo)
		}

		var adminUser models.AdminUser
		json.Unmarshal([]byte(userInfo["admin_user"].(string)), &adminUser)
		// 如果有登录记录，取出最近登录记录中的机构和应用
		lastLoginLog, getLastLoginLogErr := service.GetAdminUserLastLoginLog(adminUser.Id, app_type)
		if getLastLoginLogErr != nil {
			utils.ErrorLog("数据错误：查找mobile = %v的用户最近一次登录记录时错误：%v", mobile, getLastLoginLogErr)
		}
		currentOrgId := 0
		currentAppId := 0
		if lastLoginLog != nil {
			currentOrgId = lastLoginLog.OrgId
			currentAppId = lastLoginLog.AppId
		} else {
			if isUserInfoFromRedis {
				orgIds := userInfo["org_ids"].([]interface{})
				for _, oid := range orgIds {
					orgApps := userInfo["org_apps"].(map[string]interface{})[oid.(string)]
					for _, appMapJSON := range orgApps.(map[string]interface{}) {
						var appMap map[string]interface{}
						json.Unmarshal([]byte(appMapJSON.(string)), &appMap)

						a_t := int(appMap["app_type"].(float64))
						if a_t == app_type {
							currentOrgId = int(appMap["org_id"].(float64))
							currentAppId = int(appMap["id"].(float64))
							break
						}
					}
					if currentOrgId != 0 && currentAppId != 0 {
						break
					}
				}

			} else {
				orgIds := userInfo["org_ids"].([]string)
				for _, oid := range orgIds {
					orgApps := userInfo["org_apps"].(map[string]interface{})[oid]
					for _, appMapJSON := range orgApps.(map[string]string) {
						var appMap map[string]interface{}
						json.Unmarshal([]byte(appMapJSON), &appMap)

						a_t := int(appMap["app_type"].(float64))
						if a_t == app_type {
							currentOrgId = int(appMap["org_id"].(float64))
							currentAppId = int(appMap["id"].(float64))
							break
						}
					}
					if currentOrgId != 0 && currentAppId != 0 {
						break
					}
				}
			}
		}
		// for key, value := range userInfo {
		// 	if key == "purviews" || key == "purview_urlfors" {
		// 		continue
		// 	}
		// 	utils.TraceLog("%v: %v", key, value)
		// }
		utils.TraceLog("lastLoginLog : %v", lastLoginLog)
		utils.TraceLog("currentOrgId : %v", currentOrgId)
		utils.TraceLog("currentAppId : %v", currentAppId)

		if currentOrgId == 0 || currentAppId == 0 {
			if adminUser.IsSuperAdmin == true {
				this.Data["json"] = enums.MakeFailResponseJSONWithSGJErrorCode(enums.ErrorCodeNeverCreateTypeApp)
				this.ServeJSON()
			} else {
				this.Data["json"] = enums.MakeFailResponseJSONWithSGJErrorCode(enums.ErrorCodeContactSuperAdminCreateTypeApp)
				this.ServeJSON()
			}
			return
		}

		returnJSON := make(map[string]interface{})
		returnJSON["admin_user"] = userInfo["admin_user"]
		returnJSON["org_ids"] = userInfo["org_ids"]
		returnJSON["orgs"] = userInfo["orgs"]
		returnJSON["org_app_ids"] = userInfo["org_app_ids"]
		returnJSON["org_apps"] = userInfo["org_apps"]
		returnJSON["app_to_org_ids"] = userInfo["app_to_org_ids"]
		returnJSON["current_org_id"] = strconv.Itoa(currentOrgId)
		returnJSON["current_app_id"] = strconv.Itoa(currentAppId)
		returnJSON["purviews"] = userInfo["purviews"]
		returnJSON["purview_urlfors"] = userInfo["purview_urlfors"]
		returnJSON["app_roles"] = userInfo["app_roles"]
		returnJSON["org_subscibes"] = userInfo["org_subscibes"]
		this.Data["json"] = enums.MakeSuccessResponseJSON(returnJSON)
		this.ServeJSON()

		// 插入一条登录记录
		ip := this.GetString("ip")
		loginLog := &models.AdminUserLoginLog{
			AdminUserId: adminUser.Id,
			OrgId:       currentOrgId,
			AppId:       currentAppId,
			IP:          ip,
			OperateType: 1,
			AppType:     int8(app_type),
			CreateTime:  time.Now().Unix(),
		}
		if insertErr := service.InsertLoginLog(loginLog); insertErr != nil {
			utils.ErrorLog("为手机号为%v的用户插入一条登录记录失败：%v", mobile, insertErr)
		}

	} else {
		this.Data["json"] = enums.MakeFailResponseJSONWithSGJErrorCode(enums.ErrorCodeInvalidToken)
		this.ServeJSON()
	}
}

// 验证 token 之后需要返回的管理员用户的所有信息，包括：基本用户信息，所属的所有机构，机构下的所有应用
// map 的数据格式为
/*
"admin_user": { AdminUser's json },
"org_ids": [1, 2, 3],
"orgs": { (org_id: Org_Obj)
	1: { Org's json },
	2: { Org's json },
},
"org_app_ids": { (org_id: org_app_ids)
	1: [11, 12, 13],
	2: [21, 22, 23],
},
"org_apps": { (org_id: {app_id: OrgApp_Obj})
	1: {
		11: { OrgApp's json },
		12: { OrgApp's json },
	},
	2: {
		21: { OrgApp's json },
		22: { OrgApp's json },
	},
},
"app_to_org_ids": { (app_id: org_id)
	11: 1,
	12: 1,
	21: 2,
	22: 2,
},
"app_roles": { (app_id: App_Role Obj)
	11: {App_Role's json},
	12: {App_Role's json},
	21: {App_Role's json},
},
"purviews": { (app_id: [processed Purviews' json])
	11: [
		{Purview's json .childs[
			{Purview's json},
			{Purview's json},
		]},
		{Purview's json},
	],
	12: [
		{Purview's json},
		{Purview's json},
	],
}
"purview_urlfors": { (app_id: [url_for])
	11: [
		"Controller1.Action1",
		"Controller1.Action2",
		"Controller2.Action1",
		"Controller2.Action2",
	],
}
应当注意的是，屈服于 Golang 令人恶心的类型机制，这里将所有数值型的 key 或 value 全部转成了 string
*/
func (this *VerifyTokenController) GetAdminUserAllInfo(mobile string) (map[string]interface{}, *enums.SGJError) {
	adminUser := service.GetAdminUserWithMobile(mobile)
	if adminUser == nil {
		utils.ErrorLog("数据错误：查找不到mobile = %v的用户", mobile)
		return nil, &enums.SGJError{Code: enums.ErrorCodeDataException}
	}
	orgs, getOrgErr := service.GetAdminUserAllOrgWithUID(adminUser.Id, adminUser.IsSuperAdmin)
	if getOrgErr != nil {
		utils.ErrorLog("数据错误：查找不到mobile = %v的用户所属的机构：%v", mobile, getOrgErr)
		return nil, &enums.SGJError{Code: enums.ErrorCodeDataException}
	}
	if len(orgs) == 0 {
		utils.ErrorLog("数据错误：查找不到mobile = %v的用户所属的机构", mobile)
		return nil, &enums.SGJError{Code: enums.ErrorCodeDataException}
	}
	orgIds := make([]string, 0, len(orgs))
	orgJSONs := make(map[string]string)
	orgAppIds := make(map[string]interface{})
	orgApps := make(map[string]interface{})
	app_to_org_ids := make(map[string]string)
	app_purviewJSONs := make(map[string]string)
	app_purview_urls := make(map[string]string)
	app_roles := make(map[string]string)
	org_subscibes := make(map[string]string)
	for _, org := range orgs {
		apps, getAppsErr := service.GetAdminUserAllOrgApp(adminUser.Id, org.Id)
		if getAppsErr != nil {
			utils.ErrorLog("数据错误：查找mobile = %v的用户所属机构ID为%v下的应用时错误：%v", mobile, org.Id, getAppsErr)
			return nil, &enums.SGJError{Code: enums.ErrorCodeDataException}
		}
		if adminUser.IsSuperAdmin {
			didCreateNewApp, createAppErr := this._createAppIfNeeded(adminUser.Id, org.Id, apps)
			if createAppErr != nil {
				return nil, createAppErr
			}
			if didCreateNewApp {
				apps, getAppsErr = service.GetAdminUserAllOrgApp(adminUser.Id, org.Id)
				if getAppsErr != nil {
					utils.ErrorLog("数据错误：查找mobile = %v的用户所属机构ID为%v下的应用时错误：%v", mobile, org.Id, getAppsErr)
					return nil, &enums.SGJError{Code: enums.ErrorCodeDataException}
				}
			}

		} else {
			if len(apps) == 0 {
				continue
			}
		}

		subscibe, getSubscibeErr := service.GetOrgServeSubscibe(org.Id)
		if getSubscibeErr != nil {
			utils.ErrorLog("获取机构订阅信息失败：%v", getSubscibeErr)
			return nil, &enums.SGJError{Code: enums.ErrorCodeDataException}
		} else if subscibe == nil {
			now := time.Now()
			nextMonthDate := now.AddDate(0, 0, 30)
			subscibe = &models.ServeSubscibe{
				OrgId:       int64(org.Id),
				PeriodStart: now.Unix(),
				PeriodEnd:   nextMonthDate.Unix(),
				Status:      1,
				CreatedTime: now.Unix(),
				UpdatedTime: now.Unix(),
				State:       2,
			}
			createErr := service.CreateOrgServeSubscibe(subscibe)
			if createErr != nil {
				utils.ErrorLog(" 创建机构订阅信息失败：%v", createErr)
				return nil, &enums.SGJError{Code: enums.ErrorCodeDataException}
			}
		}
		subscibeJSON_b, _ := json.Marshal(subscibe)
		subscibeJSON := string(subscibeJSON_b)
		org_subscibes[strconv.Itoa(org.Id)] = subscibeJSON

		orgJSON_b, _ := json.Marshal(org)
		orgJSONStr := string(orgJSON_b)
		orgJSONs[strconv.Itoa(org.Id)] = orgJSONStr
		orgIds = append(orgIds, strconv.Itoa(org.Id))

		appIds := make([]string, 0, len(apps))
		appJSONs := make(map[string]string)
		for _, app := range apps {
			appJSON_b, _ := json.Marshal(app)
			appJSONStr := string(appJSON_b)
			appJSONs[strconv.Itoa(app.Id)] = appJSONStr
			appIds = append(appIds, strconv.Itoa(app.Id))
			app_to_org_ids[strconv.Itoa(app.Id)] = strconv.Itoa(org.Id)

			if adminUser.IsSuperAdmin {
				urlfors, purviews, getPurviewErr := service.GetSuperAdminUsersPurviewTreeAndUrlfors(app.AppType)
				if getPurviewErr != nil {
					utils.ErrorLog("数据错误：查找超级管理员的类型为%v的应用的权限时错误：%v", app.AppType, getPurviewErr)
					return nil, &enums.SGJError{Code: enums.ErrorCodeDataException}
				} else {
					purviewJSONs_b, _ := json.Marshal(purviews)
					app_purviewJSONs[strconv.Itoa(app.Id)] = string(purviewJSONs_b)
					urlforJSON_b, _ := json.Marshal(urlfors)
					app_purview_urls[strconv.Itoa(app.Id)] = string(urlforJSON_b)
				}
			} else {
				urlfors, purviews, getPurviewErr := service.GetGeneralAdminUsersPurviewTreeAndUrlfors(adminUser.Id, app.Id)
				if getPurviewErr != nil {
					utils.ErrorLog("数据错误：查找id为%v普通管理员的id为%v的应用的权限时错误：%v", adminUser.Id, app.Id, getPurviewErr)
					return nil, &enums.SGJError{Code: enums.ErrorCodeDataException}
				} else {
					purviewJSONs_b, _ := json.Marshal(purviews)
					app_purviewJSONs[strconv.Itoa(app.Id)] = string(purviewJSONs_b)
					urlforJSON_b, _ := json.Marshal(urlfors)
					app_purview_urls[strconv.Itoa(app.Id)] = string(urlforJSON_b)
				}
			}

			appRole, getAppRoleErr := service.GetAppRole(adminUser.Id, org.Id, app.Id)
			if getAppRoleErr != nil {
				utils.ErrorLog("数据错误：查找id=%v,orgid=%v,appid=%v的用户信息时失败：%v", adminUser.Id, org.Id, app.Id, getAppRoleErr)
				return nil, &enums.SGJError{Code: enums.ErrorCodeDataException}
			}
			appRoleJSON_b, _ := json.Marshal(appRole)
			app_roles[strconv.Itoa(app.Id)] = string(appRoleJSON_b)
		}
		orgAppIds[strconv.Itoa(org.Id)] = appIds
		orgApps[strconv.Itoa(org.Id)] = appJSONs
	}

	adminUserJSON_b, _ := json.Marshal(adminUser)
	info := make(map[string]interface{})
	info["admin_user"] = string(adminUserJSON_b)
	info["org_ids"] = orgIds
	info["orgs"] = orgJSONs
	info["org_app_ids"] = orgAppIds
	info["org_apps"] = orgApps
	info["app_to_org_ids"] = app_to_org_ids
	info["app_roles"] = app_roles
	info["purviews"] = app_purviewJSONs
	info["purview_urlfors"] = app_purview_urls
	info["org_subscibes"] = org_subscibes
	return info, nil
}

func (this *VerifyTokenController) _createAppIfNeeded(adminUserID int, orgID int, didCreatedApps []*models.OrgApp) (bool, *enums.SGJError) {
	// 已创建的应用的信息
	did_patient_manage_create := false
	did_dialysis_manage_create := false
	did_cdm_manage_create := false
	did_mall_manage_create := false
	for _, app := range didCreatedApps {
		if app.AppType == 1 {
			did_patient_manage_create = true
		} else if app.AppType == 3 {
			did_dialysis_manage_create = true
		} else if app.AppType == 4 {
			did_cdm_manage_create = true
		} else if app.AppType == 5 {
			did_mall_manage_create = true
		}
	}

	// 自动创建所有应用
	didCreateNew := false
	if did_patient_manage_create == false {
		err := service.CreateOrgApp(adminUserID, orgID, 1)
		if err != nil {
			utils.ErrorLog("自动创建酷医聚客应用失败:%v", err)
			return false, &enums.SGJError{Code: enums.ErrorCodeDataException}
		}
		didCreateNew = true
	}
	if did_dialysis_manage_create == false {
		err := service.CreateOrgApp(adminUserID, orgID, 3)
		if err != nil {
			utils.ErrorLog("自动创建透析管理应用失败:%v", err)
			return false, &enums.SGJError{Code: enums.ErrorCodeDataException}
		}
		didCreateNew = true
	}
	if did_cdm_manage_create == false {
		err := service.CreateOrgApp(adminUserID, orgID, 4)
		if err != nil {
			utils.ErrorLog("自动创建慢病管理应用失败:%v", err)
			return false, &enums.SGJError{Code: enums.ErrorCodeDataException}
		}
		didCreateNew = true
	}
	if did_mall_manage_create == false {
		err := service.CreateOrgApp(adminUserID, orgID, 5)
		if err != nil {
			utils.ErrorLog("自动创建微商城应用失败:%v", err)
			return false, &enums.SGJError{Code: enums.ErrorCodeDataException}
		}
		didCreateNew = true
	}
	return didCreateNew, nil
}
