// The MIT License
//
// Copyright (c) 2020 Temporal Technologies Inc.  All rights reserved.
//
// Copyright (c) 2020 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package internal

import (
	"context"
	"time"
)

type (
	// WorkerOptions is used to configure a worker instance.
	// The current timeout resolution implementation is in seconds and uses math.Ceil(d.Seconds()) as the duration. But is
	// subjected to change in the future.
	WorkerOptions struct {
		// Optional: To set the maximum concurrent activity executions this worker can have.
		// The zero value of this uses the default value.
		// default: defaultMaxConcurrentActivityExecutionSize(1k)
		MaxConcurrentActivityExecutionSize int

		// Optional: Sets the rate limiting on number of activities that can be executed per second per
		// worker. This can be used to limit resources used by the worker.
		// Notice that the number is represented in float, so that you can set it to less than
		// 1 if needed. For example, set the number to 0.1 means you want your activity to be executed
		// once for every 10 seconds. This can be used to protect down stream services from flooding.
		// The zero value of this uses the default value
		// default: 100k
		WorkerActivitiesPerSecond float64

		// Optional: To set the maximum concurrent local activity executions this worker can have.
		// The zero value of this uses the default value.
		// default: 1k
		MaxConcurrentLocalActivityExecutionSize int

		// Optional: Sets the rate limiting on number of local activities that can be executed per second per
		// worker. This can be used to limit resources used by the worker.
		// Notice that the number is represented in float, so that you can set it to less than
		// 1 if needed. For example, set the number to 0.1 means you want your local activity to be executed
		// once for every 10 seconds. This can be used to protect down stream services from flooding.
		// The zero value of this uses the default value
		// default: 100k
		WorkerLocalActivitiesPerSecond float64

		// Optional: Sets the rate limiting on number of activities that can be executed per second.
		// This is managed by the server and controls activities per second for your entire taskqueue
		// whereas WorkerActivityTasksPerSecond controls activities only per worker.
		// Notice that the number is represented in float, so that you can set it to less than
		// 1 if needed. For example, set the number to 0.1 means you want your activity to be executed
		// once for every 10 seconds. This can be used to protect down stream services from flooding.
		// The zero value of this uses the default value.
		// default: 100k
		TaskQueueActivitiesPerSecond float64

		// Optional: Sets the maximum number of goroutines that will concurrently poll the
		// temporal-server to retrieve activity tasks. Changing this value will affect the
		// rate at which the worker is able to consume tasks from a task queue.
		// default: 2
		MaxConcurrentActivityTaskPollers int

		// Optional: To set the maximum concurrent workflow task executions this worker can have.
		// The zero value of this uses the default value.
		// default: defaultMaxConcurrentTaskExecutionSize(1k)
		MaxConcurrentWorkflowTaskExecutionSize int

		// Optional: Sets the maximum number of goroutines that will concurrently poll the
		// temporal-server to retrieve workflow tasks. Changing this value will affect the
		// rate at which the worker is able to consume tasks from a task queue.
		// default: 2
		MaxConcurrentWorkflowTaskPollers int

		// Optional: Enable logging in replay.
		// In the workflow code you can use workflow.GetLogger(ctx) to write logs. By default, the logger will skip log
		// entry during replay mode so you won't see duplicate logs. This option will enable the logging in replay mode.
		// This is only useful for debugging purpose.
		// default: false
		EnableLoggingInReplay bool

		// Optional: Disable sticky execution.
		// Sticky Execution is to run the workflow tasks for one workflow execution on same worker host. This is an
		// optimization for workflow execution. When sticky execution is enabled, worker keeps the workflow state in
		// memory. New workflow task contains the new history events will be dispatched to the same worker. If this
		// worker crashes, the sticky workflow task will timeout after StickyScheduleToStartTimeout, and temporal server
		// will clear the stickiness for that workflow execution and automatically reschedule a new workflow task that
		// is available for any worker to pick up and resume the progress.
		// default: false
		//
		// Deprecated: DisableStickyExecution harms performance. It will be removed soon. See SetStickyWorkflowCacheSize
		// instead.
		DisableStickyExecution bool

		// Optional: Sticky schedule to start timeout.
		// The resolution is seconds. See details about StickyExecution on the comments for DisableStickyExecution.
		// default: 5s
		StickyScheduleToStartTimeout time.Duration

		// Optional: sets root context for all activities. The context can be used to pass external dependencies
		// like DB connections to activity functions.
		// Note that this method of passing dependencies is not recommended anymore.
		// Instead, use a structure with fields that contain dependencies and activities
		// as the structure member functions. Then pass all the dependencies on the structure initialization.
		BackgroundActivityContext context.Context

		// Optional: Sets how workflow worker deals with non-deterministic history events
		// (presumably arising from non-deterministic workflow definitions or non-backward compatible workflow
		// definition changes) and other panics raised from workflow code.
		// default: BlockWorkflow, which just logs error but doesn't fail workflow.
		WorkflowPanicPolicy WorkflowPanicPolicy

		// Optional: worker graceful stop timeout
		// default: 0s
		WorkerStopTimeout time.Duration

		// Optional: Enable running session workers.
		// Session workers is for activities within a session.
		// Enable this option to allow worker to process sessions.
		// default: false
		EnableSessionWorker bool

		// Uncomment this option when we support automatic restablish failed sessions.
		// Optional: The identifier of the resource consumed by sessions.
		// It's the user's responsibility to ensure there's only one worker using this resourceID.
		// For now, if user doesn't specify one, a new uuid will be used as the resourceID.
		// SessionResourceID string

		// Optional: Sets the maximum number of concurrently running sessions the resource support.
		// default: 1000
		MaxConcurrentSessionExecutionSize int

		// Optional: If set to true, a workflow worker is not started for this
		// worker and workflows cannot be registered with this worker. Use this if
		// you only want your worker to execute activities.
		// default: false
		DisableWorkflowWorker bool

		// Optional: If set to true worker would only handle workflow tasks and local activities.
		// Non-local activities will not be executed by this worker.
		// default: false
		LocalActivityWorkerOnly bool

		// Optional: If set overwrites the client level Identify value.
		// default: client identity
		Identity string

		// Optional: If set defines maximum amount of time that workflow task will be allowed to run. Defaults to 1 sec.
		DeadlockDetectionTimeout time.Duration

		// Optional: The maximum amount of time between sending each pending heartbeat to the server. Regardless of
		// heartbeat timeout, no pending heartbeat will wait longer than this amount of time to send.
		// default: 60 seconds
		MaxHeartbeatThrottleInterval time.Duration

		// Optional: The default amount of time between sending each pending heartbeat to the server. This is used if the
		// ActivityOptions do not provide a HeartbeatTimeout. Otherwise, the interval becomes a value a bit smaller than the
		// given HeartbeatTimeout.
		// default: 30 seconds
		DefaultHeartbeatThrottleInterval time.Duration

		// Interceptors to apply to the worker. Earlier interceptors wrap later
		// interceptors.
		//
		// When worker interceptors are here and in client options, the ones in
		// client options wrap the ones here. The same interceptor should not be set
		// here and in client options.
		Interceptors []WorkerInterceptor
	}
)

// WorkflowPanicPolicy is used for configuring how worker deals with workflow
// code panicking which includes non backwards compatible changes to the workflow code without appropriate
// versioning (see workflow.GetVersion).
// The default behavior is to block workflow execution until the problem is fixed.
type WorkflowPanicPolicy int

const (
	// BlockWorkflow is the default policy for handling workflow panics and detected non-determinism.
	// This option causes workflow to get stuck in the workflow task retry loop.
	// It is expected that after the problem is discovered and fixed the workflows are going to continue
	// without any additional manual intervention.
	BlockWorkflow WorkflowPanicPolicy = iota
	// FailWorkflow immediately fails workflow execution if workflow code throws panic or detects non-determinism.
	// This feature is convenient during development.
	// WARNING: enabling this in production can cause all open workflows to fail on a single bug or bad deployment.
	FailWorkflow
)

// ReplayNamespace is namespace for replay because startEvent doesn't contain it
const ReplayNamespace = "ReplayNamespace"

// IsReplayNamespace checks if the namespace is from replay
func IsReplayNamespace(dn string) bool {
	return ReplayNamespace == dn
}

// NewWorker creates an instance of worker for managing workflow and activity executions.
// client   - client created with client.NewClient().
// taskQueue - is the task queue name you use to identify your client worker, also
//            identifies group of workflow and activity implementations that are hosted by a single worker process.
// options 	- configure any worker specific options.
func NewWorker(
	client Client,
	taskQueue string,
	options WorkerOptions,
) *AggregatedWorker {
	workflowClient, ok := client.(*WorkflowClient)
	if !ok {
		panic("Client must be created with client.NewClient()")
	}
	return NewAggregatedWorker(workflowClient, taskQueue, options)
}
