// Copyright 2020-2024 guonaihong, antlabs. All rights reserved.
//
// mit license
package timer

import (
	"log"
	"sync"
	"sync/atomic"
	"testing"
	"time"
)

// 测试AfterFunc有没有运行以及时间间隔可对
func Test_MinHeap_AfterFunc_Run(t *testing.T) {
	t.Run("1ms", func(t *testing.T) {
		tm := NewTimer(WithMinHeap())

		go tm.Run()
		count := int32(0)

		tc := make(chan time.Duration, 2)

		var mu sync.Mutex
		isClose := false
		now := time.Now()
		node1 := tm.AfterFunc(time.Millisecond, func() {

			mu.Lock()
			atomic.AddInt32(&count, 1)
			if atomic.LoadInt32(&count) <= 2 && !isClose {
				tc <- time.Since(now)
			}
			mu.Unlock()
		})

		node2 := tm.AfterFunc(time.Millisecond, func() {
			mu.Lock()
			atomic.AddInt32(&count, 1)
			if atomic.LoadInt32(&count) <= 2 && !isClose {
				tc <- time.Since(now)
			}
			mu.Unlock()
		})

		time.Sleep(time.Millisecond * 3)
		mu.Lock()
		isClose = true
		close(tc)
		node1.Stop()
		node2.Stop()
		mu.Unlock()
		for tv := range tc {
			if tv < time.Millisecond || tv > 2*time.Millisecond {
				t.Errorf("tc < time.Millisecond tc > 2*time.Millisecond")

			}
		}
		if atomic.LoadInt32(&count) != 2 {
			t.Errorf("count:%d != 2", atomic.LoadInt32(&count))
		}

	})

	t.Run("10ms", func(t *testing.T) {
		tm := NewTimer(WithMinHeap())

		go tm.Run() // 运行事件循环
		count := int32(0)
		tc := make(chan time.Duration, 2)

		var mu sync.Mutex
		isClosed := false
		now := time.Now()
		node1 := tm.AfterFunc(time.Millisecond*10, func() {
			now2 := time.Now()
			mu.Lock()
			atomic.AddInt32(&count, 1)
			if atomic.LoadInt32(&count) <= 2 && !isClosed {
				tc <- time.Since(now)
			}
			mu.Unlock()
			log.Printf("node1.Lock:%v\n", time.Since(now2))
		})
		node2 := tm.AfterFunc(time.Millisecond*10, func() {
			now2 := time.Now()
			mu.Lock()
			atomic.AddInt32(&count, 1)
			if atomic.LoadInt32(&count) <= 2 && !isClosed {
				tc <- time.Since(now)
			}
			mu.Unlock()
			log.Printf("node2.Lock:%v\n", time.Since(now2))
		})

		time.Sleep(time.Millisecond * 24)
		now3 := time.Now()
		mu.Lock()
		node1.Stop()
		node2.Stop()
		isClosed = true
		close(tc)
		mu.Unlock()

		log.Printf("node1.Stop:%v\n", time.Since(now3))
		cnt := 1
		for tv := range tc {
			left := time.Millisecond * 10 * time.Duration(cnt)
			right := time.Duration(cnt) * 2 * 10 * time.Millisecond
			if tv < left || tv > right {
				t.Errorf("index(%d) (%v)tc < %v || tc > %v", cnt, tv, left, right)
			}
			// cnt++
		}
		if atomic.LoadInt32(&count) != 2 {

			t.Errorf("count:%d != 2", atomic.LoadInt32(&count))
		}

	})

	t.Run("90ms", func(t *testing.T) {
		tm := NewTimer(WithMinHeap())
		go tm.Run()
		count := int32(0)
		tm.AfterFunc(time.Millisecond*90, func() { atomic.AddInt32(&count, 1) })
		tm.AfterFunc(time.Millisecond*90, func() { atomic.AddInt32(&count, 2) })

		time.Sleep(time.Millisecond * 180)
		if atomic.LoadInt32(&count) != 3 {
			t.Errorf("count != 3")
		}

	})
}

// 测试Schedule 运行的周期可对
func Test_MinHeap_ScheduleFunc_Run(t *testing.T) {
	t.Run("1ms", func(t *testing.T) {
		tm := NewTimer(WithMinHeap())
		go tm.Run()
		count := int32(0)

		_ = tm.ScheduleFunc(2*time.Millisecond, func() {
			log.Printf("%v\n", time.Now())
			atomic.AddInt32(&count, 1)
			if atomic.LoadInt32(&count) == 2 {
				tm.Stop()
			}
		})

		time.Sleep(time.Millisecond * 5)
		if atomic.LoadInt32(&count) != 2 {
			t.Errorf("count:%d != 2", atomic.LoadInt32(&count))
		}

	})

	t.Run("10ms", func(t *testing.T) {
		tm := NewTimer(WithMinHeap())
		go tm.Run()
		count := int32(0)
		tc := make(chan time.Duration, 2)
		var mu sync.Mutex
		isClosed := false
		now := time.Now()

		node := tm.ScheduleFunc(time.Millisecond*10, func() {
			mu.Lock()
			atomic.AddInt32(&count, 1)

			if atomic.LoadInt32(&count) <= 2 && !isClosed {
				tc <- time.Since(now)
			}
			mu.Unlock()
		})

		time.Sleep(time.Millisecond * 25)

		mu.Lock()
		close(tc)
		isClosed = true
		node.Stop()
		mu.Unlock()

		cnt := 1
		for tv := range tc {
			left := time.Millisecond * 10 * time.Duration(cnt)
			right := time.Duration(cnt) * 2 * 10 * time.Millisecond
			if tv < left || tv > right {
				t.Errorf("index(%d) (%v)tc < %v || tc > %v", cnt, tv, left, right)
			}
			cnt++
		}

		if atomic.LoadInt32(&count) != 2 {
			t.Errorf("count:%d != 2", atomic.LoadInt32(&count))
		}

	})

	t.Run("30ms", func(t *testing.T) {
		tm := NewTimer(WithMinHeap())
		go tm.Run()
		count := int32(0)
		c := make(chan bool, 1)

		node := tm.ScheduleFunc(time.Millisecond*30, func() {
			atomic.AddInt32(&count, 1)
			if atomic.LoadInt32(&count) == 2 {
				c <- true
			}
		})
		go func() {
			<-c
			node.Stop()
		}()

		time.Sleep(time.Millisecond * 70)
		if atomic.LoadInt32(&count) != 2 {
			t.Errorf("count:%d != 2", atomic.LoadInt32(&count))
		}

	})
}

// 测试Stop是否会等待正在运行的任务结束
func Test_Run_Stop(t *testing.T) {
	t.Run("1ms", func(t *testing.T) {
		tm := NewTimer(WithMinHeap())
		count := uint32(0)
		tm.AfterFunc(time.Millisecond, func() { atomic.AddUint32(&count, 1) })
		tm.AfterFunc(time.Millisecond, func() { atomic.AddUint32(&count, 1) })
		go func() {
			time.Sleep(time.Millisecond * 4)
			tm.Stop()
		}()
		tm.Run()
		if atomic.LoadUint32(&count) != 2 {
			t.Errorf("count != 2")
		}
	})
}

type curstomTest struct {
	count int32
}

func (c *curstomTest) Next(now time.Time) (rv time.Time) {
	rv = now.Add(time.Duration(c.count) * time.Millisecond * 10)
	atomic.AddInt32(&c.count, 1)
	return
}

// 验证自定义函数的运行间隔时间
func Test_CustomFunc(t *testing.T) {
	t.Run("custom", func(t *testing.T) {
		tm := NewTimer(WithMinHeap())
		// mh := tm.(*minHeap) // 最小堆
		tc := make(chan time.Duration, 2)
		now := time.Now()
		count := uint32(1)
		stop := make(chan bool, 1)
		// 自定义函数
		node := tm.CustomFunc(&curstomTest{count: 1}, func() {

			if atomic.LoadUint32(&count) == 2 {
				return
			}
			// 计算运行次数
			atomic.AddUint32(&count, 1)
			tc <- time.Since(now)
			// 关闭这个任务
			close(stop)
		})

		go func() {
			<-stop
			node.Stop()
			tm.Stop()
		}()

		tm.Run()
		close(tc)
		cnt := 1
		for tv := range tc {
			left := time.Millisecond * 10 * time.Duration(cnt)
			right := time.Duration(cnt) * 2 * 10 * time.Millisecond
			if tv < left || tv > right {
				t.Errorf("index(%d) (%v)tc < %v || tc > %v", cnt, tv, left, right)
			}
			cnt++
		}
		if atomic.LoadUint32(&count) != 2 {
			t.Errorf("count != 2")
		}

		// 正在运行的任务是比较短暂的，所以外部很难
		// if mh.runCount != int32(1) {
		// 	t.Errorf("mh.runCount:%d != 1", mh.runCount)
		// }

	})
}

// 验证运行次数是符合预期的
func Test_RunCount(t *testing.T) {
	t.Run("runcount-10ms", func(t *testing.T) {
		tm := NewTimer(WithMinHeap())
		max := 10
		go func() {
			tm.Run()
		}()

		count := uint32(0)
		for i := 0; i < max; i++ {
			tm.ScheduleFunc(time.Millisecond*10, func() {
				atomic.AddUint32(&count, 1)
			})
		}

		time.Sleep(time.Millisecond * 15)
		tm.Stop()
		if count != uint32(max) {
			t.Errorf("count:%d != %d", count, max)
		}

	})
}
