package grpc

import (
	"context"
	"fmt"
	"git.solsynth.dev/hypernet/passport/pkg/authkit/models"
	"git.solsynth.dev/hypernet/pusher/pkg/pushkit"
	"github.com/goccy/go-json"
	"github.com/rs/zerolog/log"

	"git.solsynth.dev/hypernet/passport/pkg/internal/database"
	"github.com/samber/lo"

	"git.solsynth.dev/hypernet/passport/pkg/internal/services"
	"git.solsynth.dev/hypernet/passport/pkg/proto"
)

func (v *App) NotifyUser(_ context.Context, in *proto.NotifyUserRequest) (*proto.NotifyResponse, error) {
	var err error
	var user models.Account
	if user, err = services.GetAccount(uint(in.GetUserId())); err != nil {
		return nil, fmt.Errorf("unable to get account: %v", err)
	}

	var nty pushkit.Notification
	if err = json.Unmarshal(in.GetNotify().GetData(), &nty); err != nil {
		return nil, fmt.Errorf("unable to unmarshal notification: %v", err)
	}

	notification := models.NewNotificationFromPushkit(nty)
	notification.Account = user
	notification.AccountID = user.ID

	log.Debug().Str("topic", notification.Topic).Uint("uid", notification.AccountID).Msg("Notifying user...")

	if in.GetNotify().GetUnsaved() {
		if err := services.PushNotification(notification); err != nil {
			return nil, err
		}
	} else {
		if err := services.NewNotification(notification); err != nil {
			return nil, err
		}
	}

	return &proto.NotifyResponse{
		IsSuccess: true,
	}, nil
}

func (v *App) NotifyUserBatch(_ context.Context, in *proto.NotifyUserBatchRequest) (*proto.NotifyResponse, error) {
	var err error
	var users []models.Account
	if users, err = services.GetAccountList(lo.Map(in.GetUserId(), func(item uint64, index int) uint {
		return uint(item)
	})); err != nil {
		return nil, fmt.Errorf("unable to get account: %v", err)
	}

	var nty pushkit.Notification
	if err = json.Unmarshal(in.GetNotify().GetData(), &nty); err != nil {
		return nil, fmt.Errorf("unable to unmarshal notification: %v", err)
	}

	var checklist = make(map[uint]bool, len(users))
	var notifications []models.Notification
	for _, user := range users {
		if _, ok := checklist[user.ID]; ok {
			continue
		}

		notification := models.NewNotificationFromPushkit(nty)
		notification.Account = user
		notification.AccountID = user.ID
		checklist[user.ID] = true

		notifications = append(notifications, notification)
	}

	if len(notifications) == 0 {
		return &proto.NotifyResponse{
			IsSuccess: true,
		}, nil
	}

	log.Debug().Str("topic", notifications[0].Topic).Any("uid", lo.Keys(checklist)).Msg("Notifying users...")

	if in.GetNotify().GetUnsaved() {
		services.PushNotificationBatch(notifications)
	} else {
		if err := services.NewNotificationBatch(notifications); err != nil {
			return nil, err
		}
	}

	return &proto.NotifyResponse{
		IsSuccess: true,
	}, nil
}

func (v *App) NotifyAllUser(_ context.Context, in *proto.NotifyInfoPayload) (*proto.NotifyResponse, error) {
	var users []models.Account
	if err := database.C.Find(&users).Error; err != nil {
		return nil, fmt.Errorf("unable to get account: %v", err)
	}

	var nty pushkit.Notification
	if err := json.Unmarshal(in.GetData(), &nty); err != nil {
		return nil, fmt.Errorf("unable to unmarshal notification: %v", err)
	}

	var checklist = make(map[uint]bool, len(users))
	var notifications []models.Notification
	for _, user := range users {
		if checklist[user.ID] {
			continue
		}

		notification := models.NewNotificationFromPushkit(nty)
		notification.Account = user
		notification.AccountID = user.ID
		checklist[user.ID] = true

		notifications = append(notifications, notification)
	}

	log.Debug().Str("topic", notifications[0].Topic).Any("uid", lo.Keys(checklist)).Msg("Notifying users...")

	if in.GetUnsaved() {
		services.PushNotificationBatch(notifications)
	} else {
		if err := services.NewNotificationBatch(notifications); err != nil {
			return nil, err
		}
	}

	return &proto.NotifyResponse{
		IsSuccess: true,
	}, nil
}