package workflow

import (
	"agent/src/common"
	"container/list"
	"context"
	"math"
	"sync"
	"time"
)

type taskQueue struct {
	lt         *list.List
	clientTask map[common.WsClient]*list.Element
	lock       sync.RWMutex
	maxsize    int
	hasNew     chan struct{}
}

func newTaskQueue(maxsize int) *taskQueue {
	return &taskQueue{
		lt        : list.New(),
		clientTask: make(map[common.WsClient]*list.Element),
		maxsize   : maxsize,
		hasNew    : make(chan struct{}, 1),
	}
}

func (tq *taskQueue) Push(task *Task) (ok bool, evict *Task) {
	tq.lock.Lock()
	defer tq.lock.Unlock()

	if tq.lt.Len() > tq.maxsize {
		return false, nil
	} else {
		if elem, ok := tq.clientTask[task.client]; ok {
			evict = elem.Value.(*Task)
			tq.lt.Remove(elem)
		}
		elem := tq.lt.PushBack(task)
		tq.clientTask[task.client] = elem
		select {
			case tq.hasNew <- struct{}{}:
			default: 
		}
		return true, evict
	}
}


func (tq *taskQueue) Pop() *Task {
	tq.lock.Lock()
	defer tq.lock.Unlock()

	elem := tq.lt.Front()
	if elem == nil {
		return nil
	}
	tq.lt.Remove(elem)
	task := elem.Value.(*Task)
	delete(tq.clientTask, task.client)
	return task
}

func (tq *taskQueue) Remove(client common.WsClient) {
	tq.lock.Lock()
	defer tq.lock.Unlock()
	if elem, ok := tq.clientTask[client]; ok {
		tq.lt.Remove(elem)
		delete(tq.clientTask, client)
	}
}


func (tq *taskQueue) WaitForNew(ctx context.Context) *Task {
	for {
		task := tq.Pop()
		if task != nil {
			return task
		}
		select {
			case <-ctx.Done(): return nil
			case <-tq.hasNew:
		}
	}
}

// 清除pending太久的task
func (tq *taskQueue) RemoveTimeout(timeout time.Duration) {
	now := time.Now()
	tq.lock.Lock()
	defer tq.lock.Unlock()

	size := int(math.Min(float64(len(tq.clientTask) / 4), 1000))
	cnt := 0
	for client, elem := range tq.clientTask {
		task := elem.Value.(*Task)
		if now.Sub(task.createTime) > timeout {
			tq.lt.Remove(elem)
			delete(tq.clientTask, client)
			task.SendExecFail("timeout")
		}
		cnt += 1
		if cnt > size {
			break
		}
	}
}

func (tq *taskQueue) Len() int {
	tq.lock.RLock()
	defer tq.lock.RUnlock()
	return tq.lt.Len()
}