package notifications

import (
	"context"
	"encoding/json"

	"github.com/tech/sendico/pkg/db/account"
	"github.com/tech/sendico/pkg/merrors"
	me "github.com/tech/sendico/pkg/messaging/envelope"
	ch "github.com/tech/sendico/pkg/messaging/notifications/confirmation/handler"
	np "github.com/tech/sendico/pkg/messaging/notifications/processor"
	"github.com/tech/sendico/pkg/mlogger"
	"github.com/tech/sendico/pkg/model"
	nm "github.com/tech/sendico/pkg/model/notification"
	"go.mongodb.org/mongo-driver/v2/bson"
	"go.uber.org/zap"
)

type ConfirmationCodeProcessor struct {
	logger  mlogger.Logger
	db      account.DB
	handler ch.ConfirmationCodeHandler
	event   model.NotificationEvent
}

func (ccp *ConfirmationCodeProcessor) Process(ctx context.Context, envelope me.Envelope) error {
	var msg confirmationCodePayload
	if err := json.Unmarshal(envelope.GetData(), &msg); err != nil {
		ccp.logger.Warn("Failed to unmarshal confirmation code envelope", zap.Error(err), zap.String("topic", ccp.event.ToString()))
		return err
	}

	accountRef, err := bson.ObjectIDFromHex(msg.AccountRef)
	if err != nil {
		ccp.logger.Warn("Failed to restore account id from envelope", zap.Error(err), zap.String("topic", ccp.event.ToString()), zap.String("account_ref", msg.AccountRef))
		return err
	}

	var account model.Account
	if err := ccp.db.Get(ctx, accountRef, &account); err != nil {
		ccp.logger.Warn("Failed to fetch account for confirmation code", zap.Error(err), zap.String("topic", ccp.event.ToString()), zap.String("account_ref", msg.AccountRef))
		return err
	}

	target, err := model.VPFromString(msg.Target)
	if err != nil {
		ccp.logger.Warn("Failed to parse confirmation target from envelope", zap.Error(err), zap.String("topic", ccp.event.ToString()), zap.String("target", msg.Target))
		return err
	}

	if msg.Code == "" {
		return merrors.InvalidArgument("empty confirmation code", "code")
	}
	if msg.Destination == "" {
		return merrors.InvalidArgument("empty destination", "destination")
	}

	return ccp.handler(ctx, &account, msg.Destination, target, msg.Code)
}

func (ccp *ConfirmationCodeProcessor) GetSubject() model.NotificationEvent {
	return ccp.event
}

func NewConfirmationCodeProcessor(logger mlogger.Logger, db account.DB, handler ch.ConfirmationCodeHandler) np.EnvelopeProcessor {
	return &ConfirmationCodeProcessor{
		logger:  logger.Named("confirmation_code_processor"),
		db:      db,
		handler: handler,
		event:   newConfirmationEvent(nm.NAPending),
	}
}
