package server

import (
	"context"

	"go.temporal.io/sdk/converter"
	"go.temporal.io/sdk/workflow"
)

// propagationKeyName is the key used by the propagator to pass values through the Temporal server headers
const propagationKeyName = "event-details"

type propagator struct {
	dataConverter converter.DataConverter
}

// PropagateKey is the key used to store the value in the Context object
type contextPropagatorKeyType struct{}

var contextPropagatorKey = contextPropagatorKeyType{}

type contextPropagatorValues struct {
	ProjectID int64  `json:"project_id"`
	EventID   string `json:"event_id"`
	EventType string `json:"event_type"`
}

// newContextPropagator returns a context propagator that propagates a set of
// string key-value pairs across a workflow
func newContextPropagator(dataConverter converter.DataConverter) workflow.ContextPropagator {
	return &propagator{dataConverter: dataConverter}
}

// Inject injects values from context into headers for propagation
func (p *propagator) Inject(ctx context.Context, writer workflow.HeaderWriter) error {
	value := ctx.Value(contextPropagatorKey)
	payload, err := p.dataConverter.ToPayload(value)
	if err != nil {
		return err
	}
	writer.Set(propagationKeyName, payload)
	return nil
}

// InjectFromWorkflow injects values from context into headers for propagation
func (p *propagator) InjectFromWorkflow(ctx workflow.Context, writer workflow.HeaderWriter) error {
	value := ctx.Value(contextPropagatorKey)
	payload, err := p.dataConverter.ToPayload(value)
	if err != nil {
		return err
	}
	writer.Set(propagationKeyName, payload)
	return nil
}

// Extract extracts values from headers and puts them into context
func (p *propagator) Extract(ctx context.Context, reader workflow.HeaderReader) (context.Context, error) {
	value, ok := reader.Get(propagationKeyName)
	if !ok {
		// we don't fail, but just use what we have for the ctx
		return ctx, nil
	}

	var values contextPropagatorValues
	if err := p.dataConverter.FromPayload(value, &values); err != nil {
		// we don't fail, but just use what we have for the ctx
		return ctx, nil //nolint:nilerr
	}

	return context.WithValue(ctx, contextPropagatorKey, values), nil
}

// ExtractToWorkflow extracts values from headers and puts them into context
func (p *propagator) ExtractToWorkflow(ctx workflow.Context, reader workflow.HeaderReader) (workflow.Context, error) {
	value, ok := reader.Get(propagationKeyName)
	if !ok {
		// we don't fail, but just use what we have for the ctx
		return ctx, nil
	}

	var values contextPropagatorValues
	if err := p.dataConverter.FromPayload(value, &values); err != nil {
		// we don't fail, but just use what we have for the ctx
		return ctx, nil //nolint:nilerr
	}

	return workflow.WithValue(ctx, contextPropagatorKey, values), nil
}
