// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0

package logging

import (
	"io"
	"os"
)

const (
	// DefaultProviderRootLoggerName is the default provider root logger name.
	DefaultProviderRootLoggerName string = "provider"

	// DefaultSDKRootLoggerName is the default SDK root logger name.
	DefaultSDKRootLoggerName string = "sdk"
)

// loggerKey defines context keys for locating loggers in context.Context
// it's a private type to make sure no other packages can override the key
type loggerKey string

const (
	// ProviderRootLoggerKey is the loggerKey that will hold the root
	// logger for writing logs from within provider code.
	ProviderRootLoggerKey loggerKey = "provider"

	// ProviderRootLoggerOptionsKey is the loggerKey that will hold the root
	// logger options when the root provider logger is created. This is to
	// assist creating subsystem loggers, as most options cannot be fetched and
	// a logger does not provide set methods for these options.
	ProviderRootLoggerOptionsKey loggerKey = "provider-options"

	// SDKRootLoggerKey is the loggerKey that will hold the root logger for
	// writing logs from with SDKs.
	SDKRootLoggerKey loggerKey = "sdk"

	// SDKRootLoggerOptionsKey is the loggerKey that will hold the root
	// logger options when the SDK provider logger is created. This is to
	// assist creating subsystem loggers, as most options cannot be fetched and
	// a logger does not provide set methods for these options.
	SDKRootLoggerOptionsKey loggerKey = "sdk-options"

	// SinkKey is the loggerKey that will hold the logging sink used for
	// test frameworks.
	SinkKey loggerKey = ""

	// SinkOptionsKey is the loggerKey that will hold the sink
	// logger options when the SDK provider logger is created. This is to
	// assist creating subsystem loggers, as most options cannot be fetched and
	// a logger does not provide set methods for these options.
	SinkOptionsKey loggerKey = "sink-options"

	// TFLoggerOpts is the loggerKey that will hold the LoggerOpts associated
	// with the provider root logger (at `provider.tf-logger-opts`), and the
	// provider sub-system logger (at `provider.SUBSYSTEM.tf-logger-opts`),
	// in the context.Context.
	// Note that only some LoggerOpts require to be stored this way,
	// while others use the underlying *hclog.LoggerOptions of hclog.Logger.
	TFLoggerOpts loggerKey = "tf-logger-opts"
)

// providerSubsystemLoggerKey is the loggerKey that will hold the subsystem logger
// for writing logs from within a provider subsystem.
func providerSubsystemLoggerKey(subsystem string) loggerKey {
	return ProviderRootLoggerKey + loggerKey("."+subsystem)
}

// providerRootTFLoggerOptsKey is the loggerKey that will hold
// the LoggerOpts of the provider.
func providerRootTFLoggerOptsKey() loggerKey {
	return ProviderRootLoggerKey + "." + TFLoggerOpts
}

// providerRootTFLoggerOptsKey is the loggerKey that will hold
// the LoggerOpts of a provider subsystem.
func providerSubsystemTFLoggerOptsKey(subsystem string) loggerKey {
	return providerSubsystemLoggerKey(subsystem) + "." + TFLoggerOpts
}

// providerSubsystemLoggerKey is the loggerKey that will hold the subsystem logger
// for writing logs from within an SDK subsystem.
func sdkSubsystemLoggerKey(subsystem string) loggerKey {
	return SDKRootLoggerKey + loggerKey("."+subsystem)
}

// sdkRootTFLoggerOptsKey is the loggerKey that will hold
// the LoggerOpts of the SDK.
func sdkRootTFLoggerOptsKey() loggerKey {
	return SDKRootLoggerKey + "." + TFLoggerOpts
}

// sdkSubsystemTFLoggerOptsKey is the loggerKey that will hold
// the LoggerOpts of an SDK subsystem.
func sdkSubsystemTFLoggerOptsKey(subsystem string) loggerKey {
	return sdkSubsystemLoggerKey(subsystem) + "." + TFLoggerOpts
}

var (
	// Stderr caches the original os.Stderr when the process is started.
	//
	// When go-plugin.Serve is called, it overwrites our os.Stderr with a
	// gRPC stream which Terraform ignores. This tends to be before our
	// loggers get set up, as go-plugin has no way to pass in a base
	// context, and our loggers are passed around via contexts. This leaves
	// our loggers writing to an output that is never read by anything,
	// meaning the logs get blackholed. This isn't ideal, for log output,
	// so this is our workaround: we copy stderr on init, before Serve can
	// be called, and offer an option to write to that instead of the
	// os.Stderr available at runtime.
	//
	// Ideally, this is a short-term fix until Terraform starts reading
	// from go-plugin's gRPC-streamed stderr channel, but for the moment it
	// works.
	Stderr io.Writer
)

func init() {
	Stderr = os.Stderr
}
