package api

import (
	"agent/client/events"
	"agent/commons/adata"
	"agent/commons/utils"
	"agent/commons/utime"
	"agent/defines/devent/daction"
	"agent/defines/devent/dcategory"
	"agent/defines/devent/dkind"
	"encoding/json"

	"github.com/google/uuid"
)

type AgentEventGetInfoArgs struct {
	EventUuid     UUID `json:"event_uuid"`
	DestEventUuid UUID `json:"dest_event_uuid"`
	AgentUuid     UUID `json:"agent_uuid"`
}

func EventAgentEventGetInfo(args AgentEventGetInfoArgs) (IEvent, error) {
	meta, err := utils.ConvertTo[map[string]any](args)
	if err != nil {
		return nil, err
	}
	return events.NewEvent(
		dkind.UserToService,
		[]Category{dcategory.Agent, dcategory.Event, dcategory.Info},
		daction.Get,
		meta,
	), nil
}

func EventAgentLogin(startupInfo map[string]any) (IEvent, error) {
	meta := map[string]any{
		"utime": utime.NanoNow(),
	}

	data, err := json.Marshal(startupInfo)
	if err != nil {
		return nil, err
	}

	minTrashSize := int64(0)
	maxTrashSize := int64(1024)

	return events.NewEvent(
		dkind.AgentToService,
		[]Category{dcategory.Agent},
		daction.Login,
		meta,

		NewEventOpts{
			Data: adata.NewDataFromMem(data),

			Request: &EventRequestOpts{
				Parallel:     true,
				MinTrashSize: &minTrashSize,
				MaxTrashSize: &maxTrashSize,
			},
			Response: &EventResponseOpts{
				MinTrashSize: &minTrashSize,
				MaxTrashSize: &maxTrashSize,

				//Raw: utils.PointerTo(true),
				//DataOnly: utils.PointerTo(true),
				//ToFilePath: `tmp/agent_login.resp`,
			},
		},
	), nil
}

func EventAgentPing(utime int64) IEvent {
	meta := map[string]any{}

	if utime > 0 {
		meta["utime"] = utime
	}

	minTrashSize := int64(32)
	maxTrashSize := int64(128)

	return events.NewEvent(
		dkind.AgentToService,
		[]Category{dcategory.Agent},
		daction.Ping,
		meta,

		NewEventOpts{
			Request: &EventRequestOpts{
				Parallel:     true,
				MinTrashSize: &minTrashSize,
				MaxTrashSize: &maxTrashSize,
			},
			Response: &EventResponseOpts{
				MinTrashSize: &minTrashSize,
				MaxTrashSize: &maxTrashSize,
			},
		},
	)
}

func EventAgentConfig() IEvent {
	meta := map[string]any{}

	minTrashSize := int64(0)
	maxTrashSize := int64(1024)

	return events.NewEvent(
		dkind.AgentToService,
		[]Category{dcategory.Agent, dcategory.Config},
		daction.Get,
		meta,

		NewEventOpts{
			Request: &EventRequestOpts{
				MinTrashSize: &minTrashSize,
				MaxTrashSize: &maxTrashSize,
			},
			Response: &EventResponseOpts{
				MinTrashSize: &minTrashSize,
				MaxTrashSize: &maxTrashSize,
			},
		},
	)
}

func EventAgentCreateDebug(startupInfo map[string]any) (IEvent, error) {
	meta := map[string]any{
		"utime": utime.NanoNow(),
	}
	data, err := json.Marshal(startupInfo)
	if err != nil {
		return nil, err
	}
	return events.NewEvent(
		dkind.UserToService,
		[]Category{dcategory.Service, dcategory.Agent},
		daction.Create,
		meta,

		NewEventOpts{
			Data: adata.NewDataFromMem(data),
		},
	), nil
}

type AgentGetArgs struct {
	Uuid uuid.UUID `json:"uuid"`
	Raw  *bool     `json:"raw,omitempty"`
}

func EventAgentGet(args AgentGetArgs) (IEvent, error) {
	meta := map[string]any{
		"uuid": args.Uuid,
	}
	if args.Raw != nil {
		meta["raw"] = *args.Raw
	}
	return events.NewEvent(
		dkind.UserToService,
		[]Category{dcategory.Service, dcategory.Agent},
		daction.Get,
		meta,
	), nil
}

type AgentUpdateArgs struct {
	Uuid uuid.UUID `json:"uuid"`
	Name string    `json:"name,omitempty"`
	Note *string   `json:"note,omitempty"`
}

func EventAgentUpdate(args AgentUpdateArgs) (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.Agent},
		daction.Update,
		meta,
	), nil
}

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

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

type AgentListFilterOpts struct {
	ProjectUuids    []uuid.UUID `json:"project_uuids,omitempty"`
	UserGroupUuids  []uuid.UUID `json:"user_group_uuids,omitempty"`
	AgentGroupUuids []uuid.UUID `json:"agent_group_uuids,omitempty"`
	Search          string      `json:"search,omitempty"`
	TagUuids        []uuid.UUID `json:"tag_uuids,omitempty"`
	Tags            []string    `json:"tags,omitempty"`
}

func EventAgentList(args AgentListArgs) (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.Agent},
		daction.List,
		meta,
	), nil
}

type AgentListByProjectAndUserGroupsArgs struct {
	ProjectUuids   []uuid.UUID
	UserGroupUuids []uuid.UUID
	Search         string
	TagUuids       []uuid.UUID
	Tags           []string
	Exclude        *AgentListFilterOpts
	Offset         int64
	Limit          int64
	Reverse        *bool
	Count          *bool
	Admin          *bool
	Raw            *bool
}

func EventAgentListByProjectAndUserGroups(args AgentListByProjectAndUserGroupsArgs) (IEvent, error) {
	args_ := AgentListArgs{
		Kind: "by_project_and_user_groups",
		Filter: AgentListFilterOpts{
			ProjectUuids:   args.ProjectUuids,
			UserGroupUuids: args.UserGroupUuids,
			Search:         args.Search,
			TagUuids:       args.TagUuids,
			Tags:           args.Tags,
		},
		Offset:  args.Offset,
		Limit:   args.Limit,
		Reverse: args.Reverse,
		Count:   args.Count,
		Admin:   args.Admin,
		Raw:     args.Raw,
	}
	if args.Exclude != nil {
		args_.Filter = *args.Exclude
	}
	return EventAgentList(args_)
}

type AgentListByProjectAndAgentGroupsArgs struct {
	ProjectUuids    []uuid.UUID
	AgentGroupUuids []uuid.UUID
	Search          string
	TagUuids        []uuid.UUID
	Tags            []string
	Exclude         *AgentListFilterOpts
	Offset          int64
	Limit           int64
	Reverse         *bool
	Count           *bool
	Admin           *bool
	Raw             *bool
}

func EventAgentListByProjectAndAgentGroups(args AgentListByProjectAndAgentGroupsArgs) (IEvent, error) {
	args_ := AgentListArgs{
		Kind: "by_project_and_agent_groups",
		Filter: AgentListFilterOpts{
			ProjectUuids:    args.ProjectUuids,
			AgentGroupUuids: args.AgentGroupUuids,
			Search:          args.Search,
			TagUuids:        args.TagUuids,
			Tags:            args.Tags,
		},
		Offset:  args.Offset,
		Limit:   args.Limit,
		Reverse: args.Reverse,
		Count:   args.Count,
		Admin:   args.Admin,
		Raw:     args.Raw,
	}
	if args.Exclude != nil {
		args_.Filter = *args.Exclude
	}
	return EventAgentList(args_)
}

type AgentListByProjectArgs struct {
	ProjectUuids []uuid.UUID
	Search       string
	TagUuids     []uuid.UUID
	Tags         []string
	Exclude      *AgentListFilterOpts
	Offset       int64
	Limit        int64
	Reverse      *bool
	Count        *bool
	Admin        *bool
	Raw          *bool
}

func EventAgentListByProject(args AgentListByProjectArgs) (IEvent, error) {
	args_ := AgentListArgs{
		Kind: "by_project",
		Filter: AgentListFilterOpts{
			ProjectUuids: args.ProjectUuids,
			Search:       args.Search,
			TagUuids:     args.TagUuids,
			Tags:         args.Tags,
		},
		Offset:  args.Offset,
		Limit:   args.Limit,
		Reverse: args.Reverse,
		Count:   args.Count,
		Admin:   args.Admin,
		Raw:     args.Raw,
	}
	if args.Exclude != nil {
		args_.Filter = *args.Exclude
	}
	return EventAgentList(args_)
}

type AgentListByUserGroupsArgs struct {
	UserGroupUuids []uuid.UUID
	Search         string
	TagUuids       []uuid.UUID
	Tags           []string
	Exclude        *AgentListFilterOpts
	Offset         int64
	Limit          int64
	Reverse        *bool
	Count          *bool
	Admin          *bool
	Raw            *bool
}

func EventAgentListByUserGroups(args AgentListByUserGroupsArgs) (IEvent, error) {
	args_ := AgentListArgs{
		Kind: "by_user_groups",
		Filter: AgentListFilterOpts{
			UserGroupUuids: args.UserGroupUuids,
			Search:         args.Search,
			TagUuids:       args.TagUuids,
			Tags:           args.Tags,
		},
		Offset:  args.Offset,
		Limit:   args.Limit,
		Reverse: args.Reverse,
		Count:   args.Count,
		Admin:   args.Admin,
		Raw:     args.Raw,
	}
	if args.Exclude != nil {
		args_.Filter = *args.Exclude
	}
	return EventAgentList(args_)
}

type AgentListByAgentGroupsArgs struct {
	AgentGroupUuids []uuid.UUID
	Search          string
	TagUuids        []uuid.UUID
	Tags            []string
	Exclude         *AgentListFilterOpts
	Offset          int64
	Limit           int64
	Reverse         *bool
	Count           *bool
	Admin           *bool
	Raw             *bool
}

func EventAgentListByAgentGroups(args AgentListByAgentGroupsArgs) (IEvent, error) {
	args_ := AgentListArgs{
		Kind: "by_agent_groups",
		Filter: AgentListFilterOpts{
			AgentGroupUuids: args.AgentGroupUuids,
			Search:          args.Search,
			TagUuids:        args.TagUuids,
			Tags:            args.Tags,
		},
		Offset:  args.Offset,
		Limit:   args.Limit,
		Reverse: args.Reverse,
		Count:   args.Count,
		Admin:   args.Admin,
		Raw:     args.Raw,
	}
	if args.Exclude != nil {
		args_.Filter = *args.Exclude
	}
	return EventAgentList(args_)
}

type AgentListAllArgs struct {
	Search   string
	TagUuids []uuid.UUID
	Tags     []string
	Exclude  *AgentListFilterOpts
	Offset   int64
	Limit    int64
	Reverse  *bool
	Count    *bool
	Admin    *bool
	Raw      *bool
}

func EventAgentListAll(args AgentListAllArgs) (IEvent, error) {
	args_ := AgentListArgs{
		Kind: "all",
		Filter: AgentListFilterOpts{
			Search:   args.Search,
			TagUuids: args.TagUuids,
			Tags:     args.Tags,
		},
		Offset:  args.Offset,
		Limit:   args.Limit,
		Reverse: args.Reverse,
		Count:   args.Count,
		Admin:   args.Admin,
		Raw:     args.Raw,
	}
	if args.Exclude != nil {
		args_.Filter = *args.Exclude
	}
	return EventAgentList(args_)
}

func EventAgentGetNote(uuid uuid.UUID) (IEvent, error) {
	meta := map[string]any{
		"uuid": uuid,
	}

	return events.NewEvent(
		dkind.UserToService,
		[]Category{dcategory.Service, dcategory.Agent, dcategory.Note},
		daction.Get,
		meta,
	), nil
}

func EventAgentSetNote(uuid uuid.UUID, note string) (IEvent, error) {
	meta := map[string]any{
		"uuid": uuid,
		"note": note,
	}

	return events.NewEvent(
		dkind.UserToService,
		[]Category{dcategory.Service, dcategory.Agent, dcategory.Note},
		daction.Set,
		meta,
	), nil
}
