// Package bus provides NATS messaging for the poller service.
//
// credential_subscriber.go subscribes to device.credential_changed.> events
// and invalidates the credential cache so the poller uses fresh credentials
// on the next poll cycle instead of waiting for the 5-minute cache TTL.
package bus

import (
	"encoding/json"
	"log/slog"

	"github.com/nats-io/nats.go"

	"github.com/staack/the-other-dude/poller/internal/vault"
)

// CredentialSubscriber listens for credential change events and invalidates
// the credential cache. This ensures the poller picks up new credentials
// within seconds of a change rather than waiting for the 5-minute TTL.
type CredentialSubscriber struct {
	nc              *nats.Conn
	credentialCache *vault.CredentialCache
	sub             *nats.Subscription
}

// NewCredentialSubscriber creates a subscriber that invalidates cached
// credentials when the backend publishes credential_changed events.
func NewCredentialSubscriber(nc *nats.Conn, credentialCache *vault.CredentialCache) *CredentialSubscriber {
	return &CredentialSubscriber{nc: nc, credentialCache: credentialCache}
}

// Start subscribes to "device.credential_changed.>" with a queue group
// so only one poller instance processes each event.
func (s *CredentialSubscriber) Start() error {
	sub, err := s.nc.QueueSubscribe("device.credential_changed.>", "credential-invalidators", s.handleEvent)
	if err != nil {
		return err
	}
	s.sub = sub
	slog.Info("credential subscriber started", "subject", "device.credential_changed.>", "queue", "credential-invalidators")
	return nil
}

// Stop unsubscribes from NATS.
func (s *CredentialSubscriber) Stop() {
	if s.sub != nil {
		if err := s.sub.Unsubscribe(); err != nil {
			slog.Warn("error unsubscribing credential subscriber", "error", err)
		}
	}
}

// handleEvent processes a credential_changed event by invalidating the
// device's entry in the credential cache.
func (s *CredentialSubscriber) handleEvent(msg *nats.Msg) {
	var event struct {
		DeviceID string `json:"device_id"`
		TenantID string `json:"tenant_id"`
	}
	if err := json.Unmarshal(msg.Data, &event); err != nil {
		slog.Warn("failed to unmarshal credential_changed event", "error", err)
		return
	}

	if event.DeviceID == "" {
		slog.Warn("credential_changed event missing device_id")
		return
	}

	s.credentialCache.Invalidate(event.DeviceID)
	slog.Info("credential cache invalidated",
		"device_id", event.DeviceID,
		"tenant_id", event.TenantID,
	)
}
