package commandctrl

import (
	"context"

	tea "github.com/charmbracelet/bubbletea"
	"github.com/go-co-op/gocron/v2"
	"github.com/pkg/errors"
	"ucl.lmika.dev/ucl"
)

type commandCtlKeyType struct{}

var commandCtlKey = commandCtlKeyType{}

type execContext struct {
	ctrl           *CommandController
	requestRefresh bool
}

func PostMsg(ctx context.Context, msg tea.Msg) {
	cmdCtl, ok := ctx.Value(commandCtlKey).(*execContext)
	if ok {
		cmdCtl.ctrl.postMessage(msg)
	}
}

func SelectedItemIndex(ctx context.Context) (int, bool) {
	cmdCtl, ok := ctx.Value(commandCtlKey).(*execContext)
	if !ok {
		return 0, false
	}

	return cmdCtl.ctrl.uiStateProvider.SelectedItemIndex(), true
}

func SetSelectedItemIndex(ctx context.Context, newIdx int) tea.Msg {
	cmdCtl, ok := ctx.Value(commandCtlKey).(*execContext)
	if !ok {
		return nil
	}

	return cmdCtl.ctrl.uiStateProvider.SetSelectedItemIndex(newIdx)
}

func GetInvoker(ctx context.Context) Invoker {
	cmdCtl, ok := ctx.Value(commandCtlKey).(*execContext)
	if !ok {
		return nil
	}

	return cmdCtl.ctrl
}

func QueueRefresh(ctx context.Context) {
	cmdCtl, ok := ctx.Value(commandCtlKey).(*execContext)
	if !ok {
		return
	}
	cmdCtl.requestRefresh = true
}

func CronScheduler(ctx context.Context) gocron.Scheduler {
	cmdCtl, ok := ctx.Value(commandCtlKey).(*execContext)
	if !ok {
		return nil
	}
	return cmdCtl.ctrl.cronScheduler
}

func ScheduleTask(ctx context.Context, task func(ctx context.Context) error) error {
	cmdCtl, ok := ctx.Value(commandCtlKey).(*execContext)
	if !ok {
		return errors.New("no command controller")
	}
	select {
	case cmdCtl.ctrl.pendingTaskChan <- pendingTask{task: task}:
		return nil
	default:
		return errors.New("task queue is full")
	}
}

func ScheduleAuxTask(ctx context.Context, descr string, task func(ctx context.Context) error) error {
	cmdCtl, ok := ctx.Value(commandCtlKey).(*execContext)
	if !ok {
		return errors.New("no command controller")
	}
	select {
	case cmdCtl.ctrl.pendingAuxTaskChan <- pendingTask{descr: descr, task: task}:
		return nil
	default:
		return errors.New("aux task queue is full")
	}
}

type Invoker interface {
	Invoke(invokable ucl.Invokable, args []any) tea.Msg
	Inst() *ucl.Inst
}
