package notify

import (
	"context"
	"encoding/json"
	"time"

	"github.com/tidbcloud/infra-provider/infra-inspector/api"
	"github.com/tidbcloud/infra-provider/infra-inspector/pkg/config"

	"cloud.google.com/go/pubsub"
	"github.com/go-logr/logr"
)

type PubSubMsg struct {
	Target   string           `json:"target"`
	Rule     api.ViolatedRule `json:"rule"`
	Resource api.Resource     `json:"resource"`
	Time     time.Time        `json:"time"`
}

var _ Reporter = &PubSubReporter{}

type PubSubReporter struct {
	cfg *config.PubSubReporterConfig

	client *pubsub.Client // TODO: close it
}

func NewPubSubReporter(cfg *config.PubSubReporterConfig) (*PubSubReporter, error) {
	err := cfg.Complete()
	if err != nil {
		return nil, err
	}

	client, err := pubsub.NewClient(context.TODO(), cfg.ProjectID)
	if err != nil {
		return nil, err
	}

	return &PubSubReporter{
		cfg:    cfg,
		client: client,
	}, nil
}

func (r *PubSubReporter) Report(ctx context.Context, result *api.Result) error {
	logger := logr.FromContextOrDiscard(ctx).WithName("PubSubReporter")

	topic := r.client.Topic(r.cfg.TopicID)
	defer topic.Stop()

	for _, violation := range result.Violations {
		ids := make([]string, 0, len(violation.ViolatedRules))

		for _, rule := range violation.ViolatedRules {
			msg := &PubSubMsg{
				Target:   result.Target,
				Rule:     rule,
				Resource: violation.Resource,
				Time:     time.Now(),
			}

			body, err := json.Marshal(msg)
			if err != nil {
				return err
			}

			id, err := topic.Publish(ctx, &pubsub.Message{Data: body}).Get(ctx)
			if err != nil {
				return err
			}
			ids = append(ids, id)
		}

		logger.Info("published messages for a resource", "ids", ids, "target", result.Target, "resource", violation.Resource.String())
	}

	return nil
}
