package api

import (
	"agent/client/events"
	"agent/commons/utils"
	"agent/defines/devent/daction"
	"agent/defines/devent/dcategory"
	"agent/defines/devent/dkind"
	"crypto/sha256"
	"encoding/hex"
)

func EventUserLogin(login string, passw string) (IEvent, error) {
	meta := map[string]any{
		"login": login,
		"passw": PasswSelfCalc(passw),
	}
	return events.NewEvent(
		dkind.UserToService,
		[]Category{dcategory.User},
		daction.Login,
		meta,
	), nil
}

type UserLogoutArgs struct {
	All     *bool    `json:"all,omitempty"`
	Current *bool    `json:"current,omitempty"`
	Tokens  []string `json:"tokens,omitempty"`
}

func EventUserLogout(args UserLogoutArgs) (IEvent, error) {
	meta, err := utils.ConvertTo[map[string]any](args)
	if err != nil {
		return nil, err
	}
	return events.NewEvent(
		dkind.UserToService,
		[]Category{dcategory.User},
		daction.Logout,
		meta,
	), nil
}

func EventUserPing(utime int64) IEvent {
	meta := map[string]any{}
	if utime > 0 {
		meta["utime"] = utime
	}
	return events.NewEvent(
		dkind.UserToService,
		[]Category{dcategory.User},
		daction.Ping,
		meta,
	)
}

func EventUserTick() IEvent {
	return events.NewEvent(
		dkind.UserToService,
		[]Category{dcategory.Stream},
		daction.Tick,
		nil,
	)
}

func EventUserConfig() IEvent {
	meta := map[string]any{}
	return events.NewEvent(
		dkind.UserToService,
		[]Category{dcategory.User, dcategory.Config},
		daction.Get,
		meta,
	)
}

type UserCreateArgs struct {
	Uuid     UUID           `json:"uuid,omitempty"`
	Name     string         `json:"name"`
	Login    string         `json:"login"`
	Passw    string         `json:"passw"`
	Settings map[string]any `json:"settings,omitempty"`
}

func EventUserCreate(args UserCreateArgs) (IEvent, error) {
	if args.Passw != "" {
		args.Passw = PasswSelfCalc(args.Passw)
	}
	meta, err := utils.ConvertTo[map[string]any](args)
	if err != nil {
		return nil, err
	}
	return events.NewEvent(
		dkind.UserToService,
		[]Category{dcategory.Service, dcategory.User},
		daction.Create,
		meta,
	), nil
}

func EventUserDelete(uuid UUID) (IEvent, error) {
	meta := map[string]any{
		"uuid": uuid,
	}
	return events.NewEvent(
		dkind.UserToService,
		[]Category{dcategory.Service, dcategory.User},
		daction.Delete,
		meta,
	), nil
}

type UserGetArgs struct {
	Uuid  UUID   `json:"uuid,omitempty"`
	Login string `json:"login,omitempty"`
	Token string `json:"token,omitempty"`
	Raw   *bool  `json:"raw,omitempty"`
}

func EventUserGet(args UserGetArgs) (IEvent, error) {
	meta, err := utils.ConvertTo[map[string]any](args)
	if err != nil {
		return nil, err
	}
	return events.NewEvent(
		dkind.UserToService,
		[]Category{dcategory.Service, dcategory.User},
		daction.Get,
		meta,
	), nil
}

func EventUserGetCurrent() (IEvent, error) {
	return events.NewEvent(
		dkind.UserToService,
		[]Category{dcategory.Service, dcategory.User, dcategory.Current},
		daction.Get,
		nil,
	), nil
}

type UserUpdateArgs struct {
	Uuid         UUID           `json:"uuid"`
	Name         string         `json:"name,omitempty"`
	Login        string         `json:"login,omitempty"`
	Passw        string         `json:"passw,omitempty"`
	Settings     map[string]any `json:"settings,omitempty"`
	ConfirmPassw string         `json:"confirm_passw,omitempty"`
}

func EventUserUpdate(args UserUpdateArgs) (IEvent, error) {
	if args.Passw != "" {
		args.Passw = PasswSelfCalc(args.Passw)
	}
	if args.ConfirmPassw != "" {
		args.ConfirmPassw = PasswSelfCalc(args.ConfirmPassw)
	}
	meta, err := utils.ConvertTo[map[string]any](args)
	if err != nil {
		return nil, err
	}
	return events.NewEvent(
		dkind.UserToService,
		[]Category{dcategory.Service, dcategory.User},
		daction.Update,
		meta,
	), nil
}

type UserListArgs struct {
	Kind    string             `json:"kind"`
	Filter  UserListFilterOpts `json:"filter,omitempty"`
	Offset  int64              `json:"offset,omitempty"`
	Limit   int64              `json:"limit,omitempty"`
	Raw     *bool              `json:"raw,omitempty"`
	Reverse *bool              `json:"reverse,omitempty"`
	Count   *bool              `json:"count,omitempty"`
}

type UserListFilterOpts struct {
	ProjectUuid     UUID                `json:"project_uuid,omitempty"`
	UserGroupUuids  []UUID              `json:"user_group_uuids,omitempty"`
	AgentGroupUuids []UUID              `json:"agent_group_uuids,omitempty"`
	AgentUuid       UUID                `json:"agent_uuid,omitempty"`
	Search          string              `json:"search,omitempty"`
	Admin           *bool               `json:"admin,omitempty"`
	Exclude         UserListExcludeOpts `json:"exclude,omitempty"`
}

type UserListExcludeOpts struct {
	UserGroupUuids  []UUID `json:"user_group_uuids,omitempty"`
	AgentGroupUuids []UUID `json:"agent_group_uuids,omitempty"`
	UserUuids       []UUID `json:"user_uuids,omitempty"`
	Admin           *bool  `json:"admin,omitempty"`
}

func EventUserList(args UserListArgs) (IEvent, error) {
	meta, err := utils.ConvertTo[map[string]any](args)
	if err != nil {
		return nil, err
	}
	return events.NewEvent(
		dkind.UserToService,
		[]Category{dcategory.Service, dcategory.User},
		daction.List,
		meta,
	), nil
}

type UserListByProjectAndUserGroupsArgs struct {
	ProjectUuid    UUID
	UserGroupUuids []UUID
	Search         string
	Exclude        *UserListExcludeOpts
	Offset         int64
	Limit          int64
	Raw            *bool
	Reverse        *bool
	Count          *bool
}

func EventUserListByProjectAndUserGroups(args UserListByProjectAndUserGroupsArgs) (IEvent, error) {
	args_ := UserListArgs{
		Kind: "by_project_and_user_groups",
		Filter: UserListFilterOpts{
			ProjectUuid:    args.ProjectUuid,
			UserGroupUuids: args.UserGroupUuids,
			Search:         args.Search,
		},
		Offset:  args.Offset,
		Limit:   args.Limit,
		Raw:     args.Raw,
		Reverse: args.Reverse,
		Count:   args.Count,
	}
	if args.Exclude != nil {
		args_.Filter.Exclude = *args.Exclude
	}
	return EventUserList(args_)
}

type UserListByProjectArgs struct {
	ProjectUuid UUID
	Search      string
	Exclude     *UserListExcludeOpts
	Offset      int64
	Limit       int64
	Raw         *bool
	Reverse     *bool
	Count       *bool
}

func EventUserListByProject(args UserListByProjectArgs) (IEvent, error) {
	args_ := UserListArgs{
		Kind: "by_project",
		Filter: UserListFilterOpts{
			ProjectUuid: args.ProjectUuid,
			Search:      args.Search,
		},
		Offset:  args.Offset,
		Limit:   args.Limit,
		Raw:     args.Raw,
		Reverse: args.Reverse,
		Count:   args.Count,
	}
	if args.Exclude != nil {
		args_.Filter.Exclude = *args.Exclude
	}
	return EventUserList(args_)
}

type UserListByUserGroupsArgs struct {
	UserGroupUuids []UUID
	Search         string
	Exclude        *UserListExcludeOpts
	Offset         int64
	Limit          int64
	Raw            *bool
	Reverse        *bool
	Count          *bool
}

func EventUserListByUserGroups(args UserListByUserGroupsArgs) (IEvent, error) {
	args_ := UserListArgs{
		Kind: "by_user_groups",
		Filter: UserListFilterOpts{
			UserGroupUuids: args.UserGroupUuids,
			Search:         args.Search,
		},
		Offset:  args.Offset,
		Limit:   args.Limit,
		Raw:     args.Raw,
		Reverse: args.Reverse,
		Count:   args.Count,
	}
	if args.Exclude != nil {
		args_.Filter.Exclude = *args.Exclude
	}
	return EventUserList(args_)
}

type UserListByAgentGroupsArgs struct {
	AgentGroupUuids []UUID
	Search          string
	Exclude         *UserListExcludeOpts
	Offset          int64
	Limit           int64
	Raw             *bool
	Reverse         *bool
	Count           *bool
}

func EventUserListByAgentGroups(args UserListByAgentGroupsArgs) (IEvent, error) {
	args_ := UserListArgs{
		Kind: "by_agent_groups",
		Filter: UserListFilterOpts{
			AgentGroupUuids: args.AgentGroupUuids,
			Search:          args.Search,
		},
		Offset:  args.Offset,
		Limit:   args.Limit,
		Raw:     args.Raw,
		Reverse: args.Reverse,
		Count:   args.Count,
	}
	if args.Exclude != nil {
		args_.Filter.Exclude = *args.Exclude
	}
	return EventUserList(args_)
}

type UserListByAgentArgs struct {
	AgentUuid UUID
	Search    string
	Exclude   *UserListExcludeOpts
	Offset    int64
	Limit     int64
	Raw       *bool
	Reverse   *bool
	Count     *bool
}

func EventUserListByAgent(args UserListByAgentArgs) (IEvent, error) {
	args_ := UserListArgs{
		Kind: "by_agent",
		Filter: UserListFilterOpts{
			AgentUuid: args.AgentUuid,
			Search:    args.Search,
		},
		Offset:  args.Offset,
		Limit:   args.Limit,
		Raw:     args.Raw,
		Reverse: args.Reverse,
		Count:   args.Count,
	}
	if args.Exclude != nil {
		args_.Filter.Exclude = *args.Exclude
	}
	return EventUserList(args_)
}

type UserListAllArgs struct {
	Search  string
	Exclude *UserListExcludeOpts
	Offset  int64
	Limit   int64
	Raw     *bool
	Reverse *bool
	Count   *bool
}

func EventUserListAll(args UserListAllArgs) (IEvent, error) {
	args_ := UserListArgs{
		Kind: "all",
		Filter: UserListFilterOpts{
			Search: args.Search,
		},
		Offset:  args.Offset,
		Limit:   args.Limit,
		Raw:     args.Raw,
		Reverse: args.Reverse,
		Count:   args.Count,
	}
	if args.Exclude != nil {
		args_.Filter.Exclude = *args.Exclude
	}
	return EventUserList(args_)
}

func PasswSelfCalc(passw string) string {
	for i := 0; i < 255; i++ {
		passw = sha256get(passw)
	}
	return passw
}

func sha256get(text string) string {
	h := sha256.New()
	h.Write([]byte(text))
	return hex.EncodeToString(h.Sum(nil))
}
