package memtable

import (
	"bytes"
	"math/rand"
	"sync"
	"sync/atomic"
)

const (
	maxLevel    = 20
	probability = 0.25
)

// SkipList is an ordered in-memory key-value index.
//
// Writers are serialized by mu. Readers never take mu: every next pointer is
// published with atomic.Pointer.Store and read with atomic.Pointer.Load, giving
// release/acquire ordering for fully initialized nodes.
type SkipList struct {
	arena *Arena
	mu    sync.Mutex
	head  *skipNode

	height atomic.Int32
	length atomic.Uint64
}

type skipNode struct {
	next [maxLevel]atomic.Pointer[skipNode]

	key      []byte
	value    []byte
	sequence uint64
	height   int
	pending  bool
}

// NewSkipList creates an empty skip list. The arena is retained for the
// memtable API and future arena-backed nodes; Phase 1 stores nodes on the Go
// heap so atomic.Pointer can provide correct lock-free reads.
func NewSkipList(arena *Arena) *SkipList {
	head := &skipNode{height: maxLevel}
	sl := &SkipList{
		arena: arena,
		head:  head,
	}
	sl.height.Store(1)
	return sl
}

// Put inserts or replaces key with value and sequence.
func (sl *SkipList) Put(key []byte, value []byte, sequence uint64, pending bool) error {
	sl.mu.Lock()
	defer sl.mu.Unlock()

	var prev [maxLevel]*skipNode
	found := sl.findGreaterOrEqual(key, &prev)

	if found != nil && bytes.Equal(found.key, key) {
		sl.replaceLocked(&prev, found, key, value, sequence, pending)
		return nil
	}

	height := randomHeight()
	sl.ensureHeightLocked(height, &prev)
	node := newSkipNode(key, value, sequence, pending, height)

	for level := range height {
		node.next[level].Store(prev[level].next[level].Load())
	}
	for level := range height {
		prev[level].next[level].Store(node)
	}
	sl.length.Add(1)
	return nil
}

// Get returns the latest published value for key. It performs no locking.
func (sl *SkipList) Get(key []byte) (found bool, value []byte, sequence uint64) {
	node := sl.findGreaterOrEqual(key, nil)
	if node == nil || !bytes.Equal(node.key, key) || node.pending {
		return false, nil, 0
	}
	return true, cloneBytes(node.value), node.sequence
}

// Len returns the number of distinct keys in the skip list.
func (sl *SkipList) Len() uint64 {
	return sl.length.Load()
}

// NewIterator creates a forward iterator over published entries.
func (sl *SkipList) NewIterator() *Iterator {
	it := &Iterator{node: sl.head.next[0].Load()}
	it.skipPending()
	return it
}

// Iterator is a lock-free forward iterator over SkipList entries.
type Iterator struct {
	node *skipNode
}

// Valid reports whether the iterator points at an entry.
func (it *Iterator) Valid() bool {
	return it.node != nil
}

// Next advances the iterator to the next published entry.
func (it *Iterator) Next() {
	if it.node == nil {
		return
	}
	it.node = it.node.next[0].Load()
	it.skipPending()
}

// Key returns a copy of the current key.
func (it *Iterator) Key() []byte {
	if it.node == nil {
		return nil
	}
	return cloneBytes(it.node.key)
}

// Value returns a copy of the current value.
func (it *Iterator) Value() []byte {
	if it.node == nil {
		return nil
	}
	return cloneBytes(it.node.value)
}

// Sequence returns the current entry's sequence number.
func (it *Iterator) Sequence() uint64 {
	if it.node == nil {
		return 0
	}
	return it.node.sequence
}

func (it *Iterator) skipPending() {
	for it.node != nil && it.node.pending {
		it.node = it.node.next[0].Load()
	}
}

func (sl *SkipList) replaceLocked(prev *[maxLevel]*skipNode, old *skipNode, key []byte, value []byte, sequence uint64, pending bool) {
	height := randomHeight()
	sl.ensureHeightLocked(height, prev)
	node := newSkipNode(key, value, sequence, pending, height)

	for level := range maxLevel {
		oldNext := old.next[level].Load()
		if level < height {
			node.next[level].Store(oldNext)
			prev[level].next[level].Store(node)
			continue
		}

		if level < old.height {
			prev[level].next[level].Store(oldNext)
		}
	}
}

func (sl *SkipList) ensureHeightLocked(height int, prev *[maxLevel]*skipNode) {
	currentHeight := int(sl.height.Load())
	if height <= currentHeight {
		return
	}

	for level := currentHeight; level < height; level++ {
		prev[level] = sl.head
	}
	sl.height.Store(int32(height))
}

func (sl *SkipList) findGreaterOrEqual(key []byte, prev *[maxLevel]*skipNode) *skipNode {
	x := sl.head
	level := int(sl.height.Load()) - 1
	for level >= 0 {
		next := x.next[level].Load()
		if next != nil && bytes.Compare(next.key, key) < 0 {
			x = next
			continue
		}
		if prev != nil {
			prev[level] = x
		}
		level--
	}
	return x.next[0].Load()
}

func newSkipNode(key []byte, value []byte, sequence uint64, pending bool, height int) *skipNode {
	return &skipNode{
		key:      cloneBytes(key),
		value:    cloneBytes(value),
		sequence: sequence,
		height:   height,
		pending:  pending,
	}
}

func randomHeight() int {
	height := 1
	for height < maxLevel && rand.Float64() < probability {
		height++
	}
	return height
}

func cloneBytes(src []byte) []byte {
	if src == nil {
		return nil
	}
	dst := make([]byte, len(src))
	copy(dst, src)
	return dst
}
