package http

import (
	"context"
	"fmt"
	"net/http"

	log "github.com/sirupsen/logrus"

	"github.com/netbirdio/netbird/management/server"
	"github.com/netbirdio/netbird/management/server/activity"
	"github.com/netbirdio/netbird/management/server/http/api"
	"github.com/netbirdio/netbird/management/server/http/util"
	"github.com/netbirdio/netbird/management/server/jwtclaims"
)

// EventsHandler HTTP handler
type EventsHandler struct {
	accountManager  server.AccountManager
	claimsExtractor *jwtclaims.ClaimsExtractor
}

// NewEventsHandler creates a new EventsHandler HTTP handler
func NewEventsHandler(accountManager server.AccountManager, authCfg AuthCfg) *EventsHandler {
	return &EventsHandler{
		accountManager: accountManager,
		claimsExtractor: jwtclaims.NewClaimsExtractor(
			jwtclaims.WithAudience(authCfg.Audience),
			jwtclaims.WithUserIDClaim(authCfg.UserIDClaim),
		),
	}
}

// GetAllEvents list of the given account
func (h *EventsHandler) GetAllEvents(w http.ResponseWriter, r *http.Request) {
	claims := h.claimsExtractor.FromRequestContext(r)
	account, user, err := h.accountManager.GetAccountFromToken(r.Context(), claims)
	if err != nil {
		log.WithContext(r.Context()).Error(err)
		http.Redirect(w, r, "/", http.StatusInternalServerError)
		return
	}

	accountEvents, err := h.accountManager.GetEvents(r.Context(), account.Id, user.Id)
	if err != nil {
		util.WriteError(r.Context(), err, w)
		return
	}
	events := make([]*api.Event, len(accountEvents))
	for i, e := range accountEvents {
		events[i] = toEventResponse(e)
	}

	err = h.fillEventsWithUserInfo(r.Context(), events, account.Id, user.Id)
	if err != nil {
		util.WriteError(r.Context(), err, w)
		return
	}

	util.WriteJSONObject(r.Context(), w, events)
}

func (h *EventsHandler) fillEventsWithUserInfo(ctx context.Context, events []*api.Event, accountId, userId string) error {
	// build email, name maps based on users
	userInfos, err := h.accountManager.GetUsersFromAccount(ctx, accountId, userId)
	if err != nil {
		log.WithContext(ctx).Errorf("failed to get users from account: %s", err)
		return err
	}

	emails := make(map[string]string)
	names := make(map[string]string)
	for _, ui := range userInfos {
		emails[ui.ID] = ui.Email
		names[ui.ID] = ui.Name
	}

	var ok bool
	for _, event := range events {
		// fill initiator
		if event.InitiatorEmail == "" {
			event.InitiatorEmail, ok = emails[event.InitiatorId]
			if !ok {
				log.WithContext(ctx).Warnf("failed to resolve email for initiator: %s", event.InitiatorId)
			}
		}

		if event.InitiatorName == "" {
			// here to allowed to be empty because in the first release we did not store the name
			event.InitiatorName = names[event.InitiatorId]
		}

		// fill target meta
		email, ok := emails[event.TargetId]
		if !ok {
			continue
		}
		event.Meta["email"] = email

		username, ok := names[event.TargetId]
		if !ok {
			continue
		}
		event.Meta["username"] = username
	}
	return nil
}

func toEventResponse(event *activity.Event) *api.Event {
	meta := make(map[string]string)
	if event.Meta != nil {
		for s, a := range event.Meta {
			meta[s] = fmt.Sprintf("%v", a)
		}
	}
	e := &api.Event{
		Id:             fmt.Sprint(event.ID),
		InitiatorId:    event.InitiatorID,
		InitiatorName:  event.InitiatorName,
		InitiatorEmail: event.InitiatorEmail,
		Activity:       event.Activity.Message(),
		ActivityCode:   api.EventActivityCode(event.Activity.StringCode()),
		TargetId:       event.TargetID,
		Timestamp:      event.Timestamp,
		Meta:           meta,
	}
	return e
}
