// Package channels provides channel implementations for LangGraph Go.
package channels

import (
	"fmt"
	"sync"
	"sync/atomic"

	"ragflow/internal/harness/graph/errors"
)

// Missing is a sentinel value to indicate a missing value.
var Missing = &missingSentinel{}

type missingSentinel struct{}

func (m *missingSentinel) String() string {
	return "<MISSING>"
}

// IsMissing checks if a value is the Missing sentinel.
func IsMissing(val any) bool {
	if val == nil {
		return false
	}
	_, ok := val.(*missingSentinel)
	return ok
}

// Channel is the base interface for all channels.
type Channel interface {
	// GetKey returns the channel key.
	GetKey() string
	// SetKey sets the channel key.
	SetKey(key string)
	// Get returns the current value of the channel.
	// Returns EmptyChannelError if the channel is empty.
	Get() (interface{}, error)
	// IsAvailable returns true if the channel is available (not empty).
	IsAvailable() bool
	// Update updates the channel's value with the given sequence of updates.
	// Returns true if the channel was updated.
	Update(values []interface{}) (bool, error)
	// Copy returns a copy of the channel.
	Copy() Channel
	// Checkpoint returns a serializable representation of the channel's current state.
	// Returns Missing if the channel is empty.
	Checkpoint() interface{}
	// FromCheckpoint returns a new channel initialized from a checkpoint.
	FromCheckpoint(checkpoint interface{}) Channel
	// Consume notifies the channel that a subscribed task ran.
	// Returns true if the channel was updated.
	Consume() bool
	// Finish notifies the channel that the Pregel run is finishing.
	// Returns true if the channel was updated.
	Finish() bool
	// GetVersion returns the current version number of this channel.
	// Returns -1 if the channel does not support version tracking.
	GetVersion() int
}

// BaseChannel provides a base implementation of Channel.
// Embed this struct in your channel implementations.
type BaseChannel struct {
	Key     string
	Typ     interface{}
	Version int64 // atomic: channel version for change detection (thread-safe)
}

// GetKey returns the channel key.
func (c *BaseChannel) GetKey() string {
	return c.Key
}

// SetKey sets the channel key.
func (c *BaseChannel) SetKey(key string) {
	c.Key = key
}

// Consume is a no-op by default.
func (c *BaseChannel) Consume() bool {
	return false
}

// Finish is a no-op by default.
func (c *BaseChannel) Finish() bool {
	return false
}

// GetVersion returns the current version of this channel using atomic read.
// Returns -1 for channels that do not use version tracking.
func (c *BaseChannel) GetVersion() int {
	return int(atomic.LoadInt64(&c.Version))
}

// SetVersion sets the channel version atomically (used by the engine after applying writes).
func (c *BaseChannel) SetVersion(v int) {
	atomic.StoreInt64(&c.Version, int64(v))
}

// Registry is a registry of channel types.
type Registry struct {
	mu       sync.RWMutex
	channels map[string]Channel
}

// NewRegistry creates a new channel registry.
func NewRegistry() *Registry {
	return &Registry{
		channels: make(map[string]Channel),
	}
}

// Register registers a channel.
func (r *Registry) Register(name string, channel Channel) {
	r.mu.Lock()
	defer r.mu.Unlock()
	r.channels[name] = channel
}

// Get gets a channel by name.
func (r *Registry) Get(name string) (Channel, bool) {
	r.mu.RLock()
	defer r.mu.RUnlock()
	ch, ok := r.channels[name]
	return ch, ok
}

// Remove removes a channel.
func (r *Registry) Remove(name string) {
	r.mu.Lock()
	defer r.mu.Unlock()
	delete(r.channels, name)
}

// Len returns the number of channels.
func (r *Registry) Len() int {
	r.mu.RLock()
	defer r.mu.RUnlock()
	return len(r.channels)
}

// Names returns all channel names.
func (r *Registry) Names() []string {
	r.mu.RLock()
	defer r.mu.RUnlock()
	names := make([]string, 0, len(r.channels))
	for name := range r.channels {
		names = append(names, name)
	}
	return names
}

// List returns all channel names (alias for Names).
func (r *Registry) List() []string {
	return r.Names()
}

// CreateCheckpoint creates a checkpoint for all channels.
func (r *Registry) CreateCheckpoint() map[string]interface{} {
	r.mu.RLock()
	defer r.mu.RUnlock()
	checkpoint := make(map[string]interface{})
	for name, channel := range r.channels {
		cp := channel.Checkpoint()
		if !IsMissing(cp) {
			checkpoint[name] = cp
		}
	}
	return checkpoint
}

// RestoreFromCheckpoint restores all channels from a checkpoint.
func (r *Registry) RestoreFromCheckpoint(checkpoint map[string]interface{}) error {
	r.mu.Lock()
	defer r.mu.Unlock()
	for name, cp := range checkpoint {
		channel, ok := r.channels[name]
		if !ok {
			return fmt.Errorf("channel %s not found in registry", name)
		}
		newChannel := channel.FromCheckpoint(cp)
		r.channels[name] = newChannel
	}
	return nil
}

// UpdateChannels updates all channels with the given writes.
func (r *Registry) UpdateChannels(writes map[string][]interface{}) error {
	r.mu.Lock()
	defer r.mu.Unlock()
	for name, values := range writes {
		channel, ok := r.channels[name]
		if !ok {
			return &errors.ChannelNotFoundError{ChannelName: name}
		}
		if _, err := channel.Update(values); err != nil {
			return fmt.Errorf("failed to update channel %s: %w", name, err)
		}
	}
	return nil
}

// GetValues returns the current values of all channels.
func (r *Registry) GetValues() (map[string]interface{}, error) {
	r.mu.RLock()
	defer r.mu.RUnlock()
	values := make(map[string]interface{})
	for name, channel := range r.channels {
		val, err := channel.Get()
		if err != nil {
			if errors.IsEmptyChannelError(err) {
				continue
			}
			return nil, fmt.Errorf("failed to get value from channel %s: %w", name, err)
		}
		values[name] = val
	}
	return values, nil
}
