package memory

import (
	"fmt"
	"time"

	"github.com/yaoapp/gou/store"
)

// Default TTL values for each memory space
const (
	DefaultUserTTL    = 0                // No expiration for user-level memory
	DefaultTeamTTL    = 0                // No expiration for team-level memory
	DefaultChatTTL    = 24 * time.Hour   // 24 hours for chat-level memory
	DefaultContextTTL = 30 * time.Minute // 30 minutes for context-level memory
)

// New creates a new Memory instance with the given configuration and identifiers
func New(cfg *Config, userID, teamID, chatID, contextID string) (*Memory, error) {
	if cfg == nil {
		cfg = &Config{}
	}

	m := &Memory{
		UserID:    userID,
		TeamID:    teamID,
		ChatID:    chatID,
		ContextID: contextID,
		Config:    cfg,
	}

	// Initialize user namespace
	if userID != "" {
		ns, err := newNamespace(SpaceUser, userID, cfg.User, DefaultUserTTL)
		if err != nil {
			return nil, fmt.Errorf("failed to create user namespace: %w", err)
		}
		m.User = ns
	}

	// Initialize team namespace
	if teamID != "" {
		ns, err := newNamespace(SpaceTeam, teamID, cfg.Team, DefaultTeamTTL)
		if err != nil {
			return nil, fmt.Errorf("failed to create team namespace: %w", err)
		}
		m.Team = ns
	}

	// Initialize chat namespace
	if chatID != "" {
		ns, err := newNamespace(SpaceChat, chatID, cfg.Chat, DefaultChatTTL)
		if err != nil {
			return nil, fmt.Errorf("failed to create chat namespace: %w", err)
		}
		m.Chat = ns
	}

	// Initialize context namespace
	if contextID != "" {
		ns, err := newNamespace(SpaceContext, contextID, cfg.Context, DefaultContextTTL)
		if err != nil {
			return nil, fmt.Errorf("failed to create context namespace: %w", err)
		}
		m.Context = ns
	}

	return m, nil
}

// newNamespace creates a new Namespace with the given parameters
func newNamespace(space Space, id, storeID string, defaultTTL time.Duration) (*Namespace, error) {
	// Use default store ID if not specified
	if storeID == "" {
		switch space {
		case SpaceUser:
			storeID = DefaultUserStore
		case SpaceTeam:
			storeID = DefaultTeamStore
		case SpaceChat:
			storeID = DefaultChatStore
		case SpaceContext:
			storeID = DefaultContextStore
		}
	}

	// Get store instance
	s, err := store.Get(storeID)
	if err != nil {
		return nil, fmt.Errorf("failed to get store %s: %w", storeID, err)
	}

	return &Namespace{
		Space:   space,
		ID:      id,
		Store:   s,
		StoreID: storeID,
		Prefix:  fmt.Sprintf("%s:%s:", space, id),
		Default: defaultTTL,
	}, nil
}

// GetUser returns the user-level memory namespace accessor
func (m *Memory) GetUser() NamespaceAccessor {
	if m.User == nil {
		return nil
	}
	return m.User
}

// GetTeam returns the team-level memory namespace accessor
func (m *Memory) GetTeam() NamespaceAccessor {
	if m.Team == nil {
		return nil
	}
	return m.Team
}

// GetChat returns the chat-level memory namespace accessor
func (m *Memory) GetChat() NamespaceAccessor {
	if m.Chat == nil {
		return nil
	}
	return m.Chat
}

// GetContext returns the context-level memory namespace accessor
func (m *Memory) GetContext() NamespaceAccessor {
	if m.Context == nil {
		return nil
	}
	return m.Context
}

// GetSpace returns a memory namespace by space type
func (m *Memory) GetSpace(space Space) NamespaceAccessor {
	switch space {
	case SpaceUser:
		return m.GetUser()
	case SpaceTeam:
		return m.GetTeam()
	case SpaceChat:
		return m.GetChat()
	case SpaceContext:
		return m.GetContext()
	default:
		return nil
	}
}

// GetStats returns memory statistics for all namespaces
func (m *Memory) GetStats() *Stats {
	stats := &Stats{}

	if m.User != nil {
		stats.User = m.User.Stats()
	}
	if m.Team != nil {
		stats.Team = m.Team.Stats()
	}
	if m.Chat != nil {
		stats.Chat = m.Chat.Stats()
	}
	if m.Context != nil {
		stats.Context = m.Context.Stats()
	}

	return stats
}

// Clear clears all memory in all namespaces for this memory instance
func (m *Memory) Clear() {
	if m.User != nil {
		m.User.Clear()
	}
	if m.Team != nil {
		m.Team.Clear()
	}
	if m.Chat != nil {
		m.Chat.Clear()
	}
	if m.Context != nil {
		m.Context.Clear()
	}
}

// Fork creates a new Memory instance with an independent Context namespace
// but sharing the User, Team, and Chat namespaces with the parent.
// This is used for parallel agent calls (ctx.agent.All/Any/Race) to prevent
// context state from being shared between concurrent sub-agent executions.
//
// The new Context namespace uses the provided newContextID.
// If newContextID is empty, returns a shallow copy with shared Context.
func (m *Memory) Fork(newContextID string) (*Memory, error) {
	if m == nil {
		return nil, nil
	}

	// If no new context ID provided, share everything (shallow copy)
	if newContextID == "" {
		return &Memory{
			UserID:    m.UserID,
			TeamID:    m.TeamID,
			ChatID:    m.ChatID,
			ContextID: m.ContextID,
			User:      m.User,
			Team:      m.Team,
			Chat:      m.Chat,
			Context:   m.Context,
			Config:    m.Config,
		}, nil
	}

	// Create new Memory with independent Context namespace
	forked := &Memory{
		UserID:    m.UserID,
		TeamID:    m.TeamID,
		ChatID:    m.ChatID,
		ContextID: newContextID,
		User:      m.User, // Shared
		Team:      m.Team, // Shared
		Chat:      m.Chat, // Shared
		Context:   nil,    // Will be created below
		Config:    m.Config,
	}

	// Create new Context namespace with independent ID
	storeID := ""
	if m.Config != nil {
		storeID = m.Config.Context
	}
	ns, err := newNamespace(SpaceContext, newContextID, storeID, DefaultContextTTL)
	if err != nil {
		return nil, fmt.Errorf("failed to create forked context namespace: %w", err)
	}
	forked.Context = ns

	return forked, nil
}
