package leaf

import (
	"context"
	"github.com/eclipse/paho.golang/paho"
	"math"
	"time"
)

// abortIndex represents a typical value used in abort functions.
const abortIndex int8 = math.MaxInt8 >> 1

type endKeyType = string

const EndKey endKeyType = "end"

type EndType int

const (
	End = iota
	EndTimeout
	EndStop
)

type KeyValue struct {
	parent *KeyValue
	key    any
	value  any
}

var rootKeyType = &KeyValue{}

type Context struct {
	context.Context
	*paho.Publish
	engine *Engine

	index    int8
	handlers HandlersChain

	value *KeyValue
}

func WithLeafContext(c context.Context, p *paho.Publish, engine *Engine, handlers HandlersChain) *Context {
	return &Context{
		Context:  c,
		Publish:  p,
		engine:   engine,
		index:    -1,
		handlers: handlers,
		value:    rootKeyType,
	}
}

// HandlerName returns the main handler's name. For example if the handler is "handleGetUsers()",
// this function will return "main.handleGetUsers".
func (c *Context) HandlerName() string {
	return nameOfFunction(c.handlers.Last())
}

// HandlerNames returns a list of all registered handlers for this context in descending order,
// following the semantics of HandlerName()
func (c *Context) HandlerNames() []string {
	hn := make([]string, 0, len(c.handlers))
	for _, val := range c.handlers {
		if val == nil {
			continue
		}
		hn = append(hn, nameOfFunction(val))
	}
	return hn
}

// Handler returns the main handler.
func (c *Context) Handler() HandlerFunc {
	return c.handlers.Last()
}

// Hold 在当前中保留一个锚点，以便后续可以从此恢复后续处理程序。
func (c *Context) Hold() int8 {
	return c.index
}

// Resume 从 Hold 保留的锚点恢复后续处理程序。
func (c *Context) Resume(index int8) {
	c.index = index
	c.Next()
}

/************************************/
/*********** FLOW CONTROL ***********/
/************************************/

// Next should be used only inside middleware.
// It executes the pending handlers in the chain inside the calling handler.
// See example in GitHub.
func (c *Context) Next() {
	c.index++
	for c.index < int8(len(c.handlers)) {
		if c.handlers[c.index] == nil {
			continue
		}
		c.handlers[c.index](c)
		c.index++
	}
}

// IsAborted returns true if the current context was aborted.
func (c *Context) IsAborted() bool {
	return c.index >= abortIndex
}

// Abort prevents pending handlers from being called. Note that this will not stop the current handler.
// Let's say you have an authorization middleware that validates that the current request is authorized.
// If the authorization fails (ex: the password does not match), call Abort to ensure the remaining handlers
// for this request are not called.
func (c *Context) Abort() {
	c.index = abortIndex
}

func (c *Context) Done() <-chan struct{} {
	return c.Context.Done()
}

func WithValue[T any](ctx *Context, k any, v T) {
	ctx.value = &KeyValue{
		parent: ctx.value,
		key:    k,
		value:  v,
	}
}

func Value[T any](ctx *Context, k any) (v T) {
	vo := ctx.value
	for {
		if vo.key == k {
			v, _ = vo.value.(T)
			break
		} else if vo.parent == rootKeyType {
			break
		}
		vo = vo.parent
	}
	return
}

func WithCancel(ctx *Context) context.CancelFunc {
	c, cancel := context.WithCancel(ctx.Context)
	ctx.Context = c
	return cancel
}

func WithDeadline(ctx *Context, t time.Time) context.CancelFunc {
	c, cancel := context.WithDeadline(ctx.Context, t)
	ctx.Context = c
	return cancel
}
