package redis

import (
	"context"
	"encoding/json"
	"fmt"
	"log/slog"
	"time"

	goredis "github.com/redis/go-redis/v9"

	"bit.admedia.com/scm/ad/adpilot-indexing-commit-intel.com/internal/config"
	"bit.admedia.com/scm/ad/adpilot-indexing-commit-intel.com/internal/contracts/events"
	"bit.admedia.com/scm/ad/adpilot-indexing-commit-intel.com/internal/contracts/validate"
	"bit.admedia.com/scm/ad/adpilot-indexing-commit-intel.com/internal/service/commitintel"
)

// Publisher publishes commit intelligence output events to Redis Streams.
type Publisher struct {
	redis    *Client
	redisCfg config.RedisConfig
	logger   *slog.Logger
}

// NewPublisher creates a Redis Streams event publisher.
func NewPublisher(client *Client, redisCfg config.RedisConfig, logger *slog.Logger) *Publisher {
	if logger == nil {
		logger = slog.Default()
	}
	return &Publisher{
		redis:    client,
		redisCfg: redisCfg,
		logger:   logger,
	}
}

var _ commitintel.EventPublisher = (*Publisher)(nil)

// PublishCommitAnalysisReady publishes a commit.analysis.ready event.
func (p *Publisher) PublishCommitAnalysisReady(ctx context.Context, event events.CommitAnalysisReadyEvent) error {
	if err := validate.CommitAnalysisReady(event); err != nil {
		return fmt.Errorf("validate commit.analysis.ready: %w", err)
	}
	stream := p.redisCfg.StreamName(events.StreamCommitAnalysisReady)
	return p.publishJSON(ctx, stream, event.EventID, event)
}

func (p *Publisher) publishJSON(ctx context.Context, stream, eventID string, event any) error {
	payload, err := json.Marshal(event)
	if err != nil {
		return fmt.Errorf("marshal event: %w", err)
	}

	started := time.Now()
	entryID, err := p.redis.Underlying().XAdd(ctx, &goredis.XAddArgs{
		Stream: stream,
		Values: map[string]any{"payload": string(payload)},
	}).Result()
	if err != nil {
		p.logger.Error("failed to publish event",
			"stream", stream,
			"event_id", eventID,
			"error", err,
		)
		return fmt.Errorf("xadd stream %s: %w", stream, err)
	}

	p.logger.Info("published event",
		"stream", stream,
		"event_id", eventID,
		"entry_id", entryID,
		"publish_duration_ms", time.Since(started).Milliseconds(),
	)
	return nil
}
