// Copyright IBM Corp. 2020, 2026
// SPDX-License-Identifier: BUSL-1.1

package event

import (
	"net/url"
	"time"

	"github.com/hashicorp/go-hclog"
	wrapping "github.com/hashicorp/go-kms-wrapping/v2"
)

const msgField = "msg"

// getOpts - iterate the inbound Options and return a struct.
func getOpts(opt ...Option) options {
	opts := getDefaultOptions()
	for _, o := range opt {
		if o != nil {
			o(&opts)
		}
	}
	return opts
}

// Option - how Options are passed as arguments.
type Option func(*options)

// options = how options are represented
type options struct {
	withId               string
	withDetails          map[string]any
	withHeader           map[string]any
	withFlush            bool
	withInfo             map[string]any
	withRequestInfo      *RequestInfo
	withNow              time.Time
	withRequest          *Request
	withResponse         *Response
	withAuth             *Auth
	withEventer          *Eventer
	withEventerConfig    *EventerConfig
	withAllow            []string
	withDeny             []string
	withSchema           *url.URL
	withAuditWrapper     wrapping.Wrapper
	withFilterOperations AuditFilterOperations
	withGating           bool
	withNoGateLocking    bool
	withTelemetry        bool
	withCorrelationId    string

	// These options are related to the hclog adapter
	withHclogLevel hclog.Level

	withBroker          broker     // test only option
	withStderrSink      bool       // test only option
	withAuditSink       bool       // test only option
	withObservationSink bool       // test only option
	withSysSink         bool       // test only option
	withSinkFormat      SinkFormat // test only option
	withNoDefaultSink   bool       // test only option
}

func getDefaultOptions() options {
	return options{}
}

// WithId allows an optional Id
func WithId(id string) Option {
	return func(o *options) {
		o.withId = id
	}
}

// WithDetails allows an optional set of key/value pairs about an observation
// event at the detail level and observation events may have multiple "details"
func WithDetails(args ...any) Option {
	return func(o *options) {
		o.withDetails = ConvertArgs(args...)
	}
}

// WithHeader allows an optional set of key/value pairs about an event at the
// header level and observation events will only have one "header"
func WithHeader(args ...any) Option {
	return func(o *options) {
		o.withHeader = ConvertArgs(args...)
	}
}

// WithFlush allows an optional flush option.
func WithFlush() Option {
	return func(o *options) {
		o.withFlush = true
	}
}

// WithInfo allows an optional info key/value pairs about an error event.  If
// used in conjunction with the WithInfoMsg(...) option, and WithInfoMsg(...) is
// specified after WithInfo(...), then WithInfoMsg(...) will overwrite any
// values from WithInfo(...).  It's recommend that these two options not be used
// together.
func WithInfo(args ...any) Option {
	return func(o *options) {
		o.withInfo = ConvertArgs(args...)
	}
}

// WithInfoMsg allows an optional msg and optional info key/value pairs about an
// error event. If used in conjunction with the WithInfo(...) option, and
// WithInfo(...) is specified after WithInfoMsg(...), then WithInfo(...) will
// overwrite any values from WithInfo(...).  It's recommend that these two
// options not be used together.
func WithInfoMsg(msg string, args ...any) Option {
	return func(o *options) {
		o.withInfo = ConvertArgs(args...)
		if o.withInfo == nil {
			o.withInfo = map[string]any{
				msgField: msg,
			}
			return
		}
		o.withInfo[msgField] = msg
	}
}

// WithRequestInfo allows an optional RequestInfo
func WithRequestInfo(i *RequestInfo) Option {
	return func(o *options) {
		o.withRequestInfo = i
	}
}

// withCorrelationId allows an optional CorrelationId
func withCorrelationId(id string) Option {
	return func(o *options) {
		o.withCorrelationId = id
	}
}

// WithNow allows an option time.Time to represent now.
func WithNow(now time.Time) Option {
	return func(o *options) {
		o.withNow = now
	}
}

// WithRequest allows an optional request
func WithRequest(r *Request) Option {
	return func(o *options) {
		o.withRequest = r
	}
}

// WithResponse allows an optional response
func WithResponse(r *Response) Option {
	return func(o *options) {
		o.withResponse = r
	}
}

// WithAuth allows an optional Auth
func WithAuth(a *Auth) Option {
	return func(o *options) {
		o.withAuth = a
	}
}

// WithEventer allows an optional eventer
func WithEventer(e *Eventer) Option {
	return func(o *options) {
		o.withEventer = e
	}
}

// WithEventer allows an optional eventer config
func WithEventerConfig(c *EventerConfig) Option {
	return func(o *options) {
		o.withEventerConfig = c
	}
}

// WithSchema is an optional schema for the cloudevents
func WithSchema(url *url.URL) Option {
	return func(o *options) {
		o.withSchema = url
	}
}

// WithAllow is an optional set of allow filters
func WithAllow(f ...string) Option {
	return func(o *options) {
		o.withAllow = f
	}
}

// WithDeny is an optional set of deny filters
func WithDeny(f ...string) Option {
	return func(o *options) {
		o.withDeny = f
	}
}

// WithAuditWrapper is an optional wrapper for audit events
func WithAuditWrapper(w wrapping.Wrapper) Option {
	return func(o *options) {
		o.withAuditWrapper = w
	}
}

// WithFilterOperations is an optional set of filter operations
func WithFilterOperations(fop AuditFilterOperations) Option {
	return func(o *options) {
		o.withFilterOperations = fop
	}
}

// WithHclogLevel is an option to specify a log level if using the adapter
func WithHclogLevel(with hclog.Level) Option {
	return func(o *options) {
		o.withHclogLevel = with
	}
}

// WithGating starts the eventer in gated mode
func WithGating(with bool) Option {
	return func(o *options) {
		o.withGating = with
	}
}

// WithNoGateLocking is used when trawling through the existing queue to ensure we don't deadlock
func WithNoGateLocking(with bool) Option {
	return func(o *options) {
		o.withNoGateLocking = with
	}
}

// WithTelemetry allows an optional telemetry option.
func WithTelemetry() Option {
	return func(o *options) {
		o.withTelemetry = true
	}
}
