package service

import (
	"encoding/json"
	"io/ioutil"
	"net/http"
	"net/url"
	"strconv"
	"time"

	"XT_Admin_Api/models"
	"XT_Admin_Api/utils"

	"github.com/astaxie/beego"
)

type AdminUserInfo struct {
	AdminUser    *models.AdminUser                    `json:"user"`
	CurrentOrgId int64                                `json:"current_org_id"`
	CurrentAppId int64                                `json:"current_app_id"`
	OrgIds       []int64                              `json:"org_ids"`
	Orgs         map[int64]*models.Org                `json:"orgs"`
	OrgAppIds    map[int64][]int64                    `json:"org_app_ids"`
	OrgApps      map[int64](map[int64]*models.OrgApp) `json:"org_apps"`
	App2OrgIds   map[int64]int64                      `json:"app_to_org_ids"`
	AppRoles     map[int64]*models.App_Role           `json:"app_roles"`
	AppPurviews  map[int64][]*models.Purview          `json:"app_purviews"`
	AppUrlfors   map[int64][]string                   `json:"app_urlfors"`
	Subscibes    map[int64]*models.ServeSubscibe      `json:"org_subscibes"`

}

type verifyTokenError struct {
	Msg string
}

func (e *verifyTokenError) Error() string {
	return e.Msg
}

// 验证 token 成功后返回的管理员用户的所有信息，包括：基本用户信息，所属的所有机构，机构下的所有应用，应用的用户权限
// map 的数据格式为
/*
"admin_user": { AdminUser's json },
current_org_id: 1,
current_app_id: 11,
"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 VerifyToken(token string, ip string, sessionID string) (*AdminUserInfo, error, int) {
	// if len(sessionID) == 0 {
	// 	return nil, &verifyTokenError{"sessionID 为空"}
	// }
	ssoDomain := beego.AppConfig.String("sso_domain")
	api := ssoDomain + "/verifytoken"
	values := make(url.Values)
	values.Set("token", token)
	values.Set("app_type", "3")
	values.Set("ip", ip)
	values.Set("session_id", sessionID)
	resp, requestErr := http.PostForm(api, values)

	if requestErr != nil {
		utils.ErrorLog("请求验证 sso token 接口失败: %v", requestErr)
		return nil, requestErr, 0
	}
	defer resp.Body.Close()
	body, ioErr := ioutil.ReadAll(resp.Body)
	if ioErr != nil {
		utils.ErrorLog("验证 sso token 接口返回数据读取失败: %v", ioErr)
		return nil, ioErr, 0
	}
	var respJSON map[string]interface{}
	utils.InfoLog(string(body))
	if err := json.Unmarshal([]byte(string(body)), &respJSON); err != nil {
		utils.ErrorLog("验证 sso token 接口返回数据解析JSON失败: %v", err)
		return nil, err, 0
	}

	if respJSON["state"].(float64) != 1 {
		msg := respJSON["msg"].(string)
		utils.ErrorLog("验证 sso token 接口请求失败: %v", msg)
		return nil, &verifyTokenError{"验证 sso token 接口请求失败"}, int(respJSON["code"].(float64))
	} else {
		utils.SuccessLog("验证 sso token 成功")
		return processAdminUserInfo(respJSON["data"].(map[string]interface{})), nil, 0
	}
}

func processAdminUserInfo(data map[string]interface{}) *AdminUserInfo {
	adminUser := processAdminUser(data)
	currentOrgId, currentAppId := processCurrentOrgIDAndAppID(data)
	orgIds := processOrgIds(data)
	orgs := processOrgs(data)
	orgAppIds := processOrgAppIds(data)
	orgApps := processOrgApps(data)
	app2OrgIds := processApp2OrgIds(data)
	appRoles := processAppRoles(data)
	appPurviews := processPurviews(data)
	appUrlfors := processPurviewUrlfors(data)
	orgSubscibes := processOrgSubscibes(data)
	sessionAdminUserInfo := &AdminUserInfo{
		AdminUser:    adminUser,
		CurrentOrgId: currentOrgId,
		CurrentAppId: currentAppId,
		OrgIds:       orgIds,
		Orgs:         orgs,
		OrgAppIds:    orgAppIds,
		OrgApps:      orgApps,
		App2OrgIds:   app2OrgIds,
		AppRoles:     appRoles,
		AppPurviews:  appPurviews,
		AppUrlfors:   appUrlfors,
		Subscibes:    orgSubscibes,
	}
	return sessionAdminUserInfo
}

// "admin_user": { AdminUser's json },
func processAdminUser(data map[string]interface{}) *models.AdminUser {
	userJSONStr := data["admin_user"].(string)
	var adminUser models.AdminUser
	if err := json.Unmarshal([]byte(userJSONStr), &adminUser); err != nil {
		utils.ErrorLog("解析用户信息失败：%v", err)
		return nil
	} else {
		return &adminUser
	}
}

// current_org_id: 1,
// current_app_id: 11,
func processCurrentOrgIDAndAppID(data map[string]interface{}) (int64, int64) {
	orgIDStr := data["current_org_id"].(string)
	appIDStr := data["current_app_id"].(string)
	orgID, _ := strconv.Atoi(orgIDStr)
	appID, _ := strconv.Atoi(appIDStr)
	return int64(orgID), int64(appID)
}

// "org_ids": [1, 2, 3],
func processOrgIds(data map[string]interface{}) []int64 {
	orgIdStrs := data["org_ids"].([]interface{})
	orgIds := make([]int64, 0, len(orgIdStrs))
	for _, idstr := range orgIdStrs {
		id, _ := strconv.Atoi(idstr.(string))
		orgIds = append(orgIds, int64(id))
	}
	return orgIds
}

// "orgs": { (org_id: Org_Obj)
// 	1: { Org's json },
// 	2: { Org's json },
// },
func processOrgs(data map[string]interface{}) map[int64]*models.Org {
	orgJSONs := data["orgs"].(map[string]interface{})
	orgs := make(map[int64]*models.Org)
	for orgIdStr, orgJSON := range orgJSONs {
		orgId, _ := strconv.Atoi(orgIdStr)
		var org models.Org
		json.Unmarshal([]byte(orgJSON.(string)), &org)
		orgs[int64(orgId)] = &org
	}
	return orgs
}

// "org_app_ids": { (org_id: org_app_ids)
// 	1: [11, 12, 13],
// 	2: [21, 22, 23],
// },
func processOrgAppIds(data map[string]interface{}) map[int64][]int64 {
	orgAppIdStrs := data["org_app_ids"].(map[string]interface{})
	orgAppIds := make(map[int64][]int64)
	for orgIdStr, appIdStrs := range orgAppIdStrs {
		orgId, _ := strconv.Atoi(orgIdStr)
		appIds := make([]int64, 0, len(appIdStrs.([]interface{})))
		for _, appIdStr := range appIdStrs.([]interface{}) {
			appId, _ := strconv.Atoi(appIdStr.(string))
			appIds = append(appIds, int64(appId))
		}
		orgAppIds[int64(orgId)] = appIds
	}
	return orgAppIds
}

// "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 },
// 	},
// },
func processOrgApps(data map[string]interface{}) map[int64]map[int64]*models.OrgApp {
	orgAppJSONs := data["org_apps"].(map[string]interface{})
	orgApps := make(map[int64]map[int64]*models.OrgApp)
	for orgIdStr, appJSONStrMap := range orgAppJSONs {
		orgId, _ := strconv.Atoi(orgIdStr)

		apps := make(map[int64]*models.OrgApp)
		for appIdStr, appJSONStr := range appJSONStrMap.(map[string]interface{}) {
			appId, _ := strconv.Atoi(appIdStr)

			var app models.OrgApp
			json.Unmarshal([]byte(appJSONStr.(string)), &app)
			apps[int64(appId)] = &app
		}

		orgApps[int64(orgId)] = apps
	}
	return orgApps
}

// "app_to_org_ids": { (app_id: org_id)
// 	11: 1,
// 	12: 1,
// 	21: 2,
// 	22: 2,
// },
func processApp2OrgIds(data map[string]interface{}) map[int64]int64 {
	app2OrgIdStrs := data["app_to_org_ids"].(map[string]interface{})
	app2OrgIds := make(map[int64]int64)
	for appIdStr, orgIdStr := range app2OrgIdStrs {
		orgId, _ := strconv.Atoi(orgIdStr.(string))
		appId, _ := strconv.Atoi(appIdStr)
		app2OrgIds[int64(appId)] = int64(orgId)
	}
	return app2OrgIds
}

// "app_roles": { (app_id: App_Role Obj)
// 	11: {App_Role's json},
// 	12: {App_Role's json},
// 	21: {App_Role's json},
// },
func processAppRoles(data map[string]interface{}) map[int64]*models.App_Role {
	appRoleJSONs := data["app_roles"].(map[string]interface{})
	appRoles := make(map[int64]*models.App_Role)
	for appIDStr, appRoleJSON := range appRoleJSONs {
		appID, _ := strconv.Atoi(appIDStr)
		var appRole models.App_Role
		json.Unmarshal([]byte(appRoleJSON.(string)), &appRole)
		appRoles[int64(appID)] = &appRole
	}
	return appRoles
}

// "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},
// 	],
// },
func processPurviews(data map[string]interface{}) map[int64][]*models.Purview {
	appPurviewJSONsStrs := data["purviews"].(map[string]interface{})
	appPurviews := make(map[int64][]*models.Purview)
	for appIdStr, purviewJSONsStr := range appPurviewJSONsStrs {
		appId, _ := strconv.Atoi(appIdStr)
		var purviews []*models.Purview
		json.Unmarshal([]byte(purviewJSONsStr.(string)), &purviews)
		// setLinkForPurviews(purviews)
		appPurviews[int64(appId)] = purviews
	}
	return appPurviews
}

// func setLinkForPurviews(purviews []*models.Purview) {
// 	for _, purview := range purviews {
// 		if len(purview.Urlfor) == 0 {
// 			purview.Link = ""
// 		} else {
// 			purview.Link = beego.URLFor(purview.Urlfor)
// 		}
// 		if purview.Childs == nil {
// 			purview.Childs = make([]*models.Purview, 0)
// 		} else {
// 			setLinkForPurviews(purview.Childs)
// 		}
// 		// utils.TraceLog("%+v", purview)
// 	}
// }

// "purview_urlfors": { (app_id: [url_for])
// 	11: [
// 		"Controller1.Action1",
// 		"Controller1.Action2",
// 		"Controller2.Action1",
// 		"Controller2.Action2",
// 	],
// }
func processPurviewUrlfors(data map[string]interface{}) map[int64][]string {
	appUrlforsStrs := data["purview_urlfors"].(map[string]interface{})
	appUrlfors := make(map[int64][]string)
	for appIdStr, urlforsStr := range appUrlforsStrs {
		appId, _ := strconv.Atoi(appIdStr)
		var urlfors []string
		json.Unmarshal([]byte(urlforsStr.(string)), &urlfors)
		appUrlfors[int64(appId)] = urlfors
	}
	return appUrlfors
}

// "org_subscibes": { (org_id: ServeSubscibe)
//  11: {ServeSubscibe's json}
// },
func processOrgSubscibes(data map[string]interface{}) map[int64]*models.ServeSubscibe {
	subscibeJSONs := data["org_subscibes"].(map[string]interface{})
	subscibes := make(map[int64]*models.ServeSubscibe)
	for orgIDStr, subscibeJSON := range subscibeJSONs {
		orgID, _ := strconv.Atoi(orgIDStr)
		var subscibe models.ServeSubscibe
		json.Unmarshal([]byte(subscibeJSON.(string)), &subscibe)
		subscibes[int64(orgID)] = &subscibe
	}
	return subscibes
}

func ModifyPassword(adminID int64, password string) error {
	err := writeUserDb.Model(&models.AdminUser{}).Where("id = ? AND status = 1", adminID).Updates(map[string]interface{}{"password": password, "mtime": time.Now().Unix()}).Error
	return err
}
