package scim

import (
	"bytes"
	"strings"
)

const (
	UserSchema           = "urn:ietf:params:scim:schemas:core:2.0:User"
	EnterpriseUserSchema = "urn:ietf:params:scim:schemas:extension:enterprise:2.0:User"
)

type ScimUserName struct {
	GivenName  string `json:"givenName"`
	FamilyName string `json:"familyName"`
}

type ScimUserEmail struct {
	Value   string `json:"value"`
	Type    string `json:"type"`
	Primary bool   `json:"primary"`
	Display string `json:"display"`
}

type ScimEntInfo struct {
	OrgRole      string            `json:"orgRole"`
	ProjectRoles map[string]string `json:"projectRoles"`
}

type ScimUserMeta struct {
	ResourceType string `json:"resourceType"`
}

type ScimUserData map[string]interface{}

func (d ScimUserData) SetSchemas(schemas []string) {
	d["schemas"] = schemas
}

func (d ScimUserData) SetID(id string) {
	d["id"] = id
}

func (d ScimUserData) SetExternalID(id string) {
	d["externalId"] = id
}

func (d ScimUserData) GetUserName() string {
	userNameI, ok := d["userName"]
	if !ok {
		return ""
	}
	userName, ok := userNameI.(string)
	if !ok {
		return ""
	}
	return userName
}

func (d ScimUserData) SetUserName(userName string) {
	d["userName"] = userName
}

func (d ScimUserData) GetName() ScimUserName {
	scimUserName := ScimUserName{}

	nameI, ok := d["name"]
	if !ok {
		return scimUserName
	}
	nameM, ok := nameI.(map[string]interface{})
	if !ok {
		return scimUserName
	}
	if gNameI, ok := nameM["givenName"]; ok {
		gName, ok := gNameI.(string)
		if ok {
			scimUserName.GivenName = gName
		}
	}
	if fNameI, ok := nameM["familyName"]; ok {
		fName, ok := fNameI.(string)
		if ok {
			scimUserName.FamilyName = fName
		}
	}

	return scimUserName
}

func (d ScimUserData) SetName(name ScimUserName) {
	d["name"] = map[string]interface{}{
		"givenName":  name.GivenName,
		"familyName": name.FamilyName,
	}
}

func (d ScimUserData) GetEmails() []ScimUserEmail {
	var scimUserEmails []ScimUserEmail
	emailsI, ok := d["emails"]
	if !ok {
		return scimUserEmails
	}
	emailsInterface, ok := emailsI.([]interface{})
	if !ok {
		return scimUserEmails
	}
	for _, ei := range emailsInterface {
		e, ok := ei.(map[string]interface{})
		if !ok {
			continue
		}
		email := ScimUserEmail{}
		if evI, ok := e["value"]; ok {
			if eVal, ok := evI.(string); ok {
				email.Value = eVal
			}
		}
		if evI, ok := e["type"]; ok {
			if eVal, ok := evI.(string); ok {
				email.Type = eVal
			}
		}
		if evI, ok := e["primary"]; ok {
			if eVal, ok := evI.(bool); ok {
				email.Primary = eVal
			}
		}
		if evI, ok := e["display"]; ok {
			if eVal, ok := evI.(string); ok {
				email.Display = eVal
			}
		}
		scimUserEmails = append(scimUserEmails, email)
	}
	return scimUserEmails
}

func (d ScimUserData) SetEmails(emails []ScimUserEmail) {
	var m []interface{}
	for _, email := range emails {
		me := map[string]interface{}{
			"value":   email.Value,
			"type":    email.Type,
			"primary": email.Primary,
			"display": email.Display,
		}
		m = append(m, me)
	}
	d["emails"] = m
}

func (d ScimUserData) GetActive() bool {
	aI, ok := d["active"]
	if !ok {
		return false
	}
	active, ok := aI.(bool)
	if !ok {
		return false
	}
	return active
}

func (d ScimUserData) SetActive(active bool) {
	d["active"] = active
}

func (d ScimUserData) SetMeta(meta ScimUserMeta) {
	d["meta"] = map[string]interface{}{
		"resourceType": meta.ResourceType,
	}
}

func (d ScimUserData) GetEntInfo() ScimEntInfo {
	info := ScimEntInfo{}

	entSchema, ok := d[EnterpriseUserSchema]
	if !ok {
		return info
	}
	entInterface, ok := entSchema.(map[string]interface{})
	if !ok {
		return info
	}
	if orgRoleI, ok := entInterface["orgRole"]; ok {
		if orgRole, ok := orgRoleI.(string); ok {
			info.OrgRole = orgRole
		}
	}
	// 111,222,333+Project Owner;222+Project Data Access Read-Write;
	projectId2RoleMap := map[string]string{}
	if projectRolesI, ok := entInterface["projectRoles"]; ok {
		if projectRolePair, ok := projectRolesI.(string); ok {
			projectPairs := strings.Split(projectRolePair, ";")
			if len(projectPairs) > 0 {
				for _, pair := range projectPairs {
					if pair == "" {
						continue
					}
					split := strings.Split(pair, "+")
					if len(split) != 2 {
						continue
					}
					projectIdStr := split[0]
					role := split[1]
					trimRole := strings.TrimSpace(role)
					projectIdList := strings.Split(projectIdStr, ",")
					for _, pid := range projectIdList {
						if pid == "" {
							continue
						}
						trimPid := strings.TrimSpace(pid)
						projectId2RoleMap[trimPid] = trimRole
					}
				}
			}
		}
	}
	info.ProjectRoles = projectId2RoleMap

	return info
}

func (d ScimUserData) SetEntInfo(ent ScimEntInfo) {
	projectRoles := ""
	if len(ent.ProjectRoles) > 0 {
		var buffer bytes.Buffer
		for pid, role := range ent.ProjectRoles {
			buffer.WriteString(pid)
			buffer.WriteString("+")
			buffer.WriteString(role)
			buffer.WriteString(";")
		}
		projectRoles = buffer.String()
	}
	d[EnterpriseUserSchema] = map[string]interface{}{
		"orgRole":      ent.OrgRole,
		"projectRoles": projectRoles,
	}
}
