package frankenphp

import (
	"context"
	"sync"

	"github.com/dunglas/frankenphp/internal/state"
)

// representation of a thread that handles tasks directly assigned by go
// implements the threadHandler interface
type taskThread struct {
	thread   *phpThread
	execChan chan *task
}

// task callbacks will be executed directly on the PHP thread
// therefore having full access to the PHP runtime
type task struct {
	callback func()
	done     sync.Mutex
}

func newTask(cb func()) *task {
	t := &task{callback: cb}
	t.done.Lock()

	return t
}

func (t *task) waitForCompletion() {
	t.done.Lock()
}

func convertToTaskThread(thread *phpThread) *taskThread {
	handler := &taskThread{
		thread:   thread,
		execChan: make(chan *task),
	}
	thread.setHandler(handler)
	return handler
}

func (handler *taskThread) beforeScriptExecution() string {
	thread := handler.thread

	switch thread.state.Get() {
	case state.TransitionRequested:
		return thread.transitionToNewHandler()
	case state.Booting, state.TransitionComplete:
		thread.state.Set(state.Ready)
		handler.waitForTasks()

		return handler.beforeScriptExecution()
	case state.Ready:
		handler.waitForTasks()

		return handler.beforeScriptExecution()
	case state.ShuttingDown:
		// signal to stop
		return ""
	}
	panic("unexpected state: " + thread.state.Name())
}

func (handler *taskThread) afterScriptExecution(_ int) {
	panic("task threads should not execute scripts")
}

func (handler *taskThread) frankenPHPContext() *frankenPHPContext {
	return nil
}

func (handler *taskThread) context() context.Context {
	return nil
}

func (handler *taskThread) name() string {
	return "Task PHP Thread"
}

func (handler *taskThread) waitForTasks() {
	for {
		select {
		case task := <-handler.execChan:
			task.callback()
			task.done.Unlock() // unlock the task to signal completion
		case <-handler.thread.drainChan:
			// thread is shutting down, do not execute the function
			return
		}
	}
}

func (handler *taskThread) execute(t *task) {
	handler.execChan <- t
}
