package output

import (
	"fmt"
	"sync"

	"github.com/yaoapp/yao/agent/context"
	"github.com/yaoapp/yao/agent/output/adapters/cui"
	"github.com/yaoapp/yao/agent/output/adapters/openai"
	"github.com/yaoapp/yao/agent/output/message"
)

var (
	writerCache   = make(map[*context.Context]message.Writer)
	writerMutex   sync.RWMutex
	globalFactory message.WriterFactory
)

// Send sends a single message using the appropriate writer for the context
func Send(ctx *context.Context, msg *message.Message) error {
	writer, err := GetWriter(ctx)
	if err != nil {
		return err
	}
	return writer.Write(msg)
}

// SendGroup sends a message group using the appropriate writer for the context
func SendGroup(ctx *context.Context, group *message.MessageGroup) error {
	writer, err := GetWriter(ctx)
	if err != nil {
		return err
	}
	return writer.WriteGroup(group)
}

// GetWriter gets or creates a writer for the given context
// Writers are cached per context to avoid recreating them
func GetWriter(ctx *context.Context) (message.Writer, error) {
	// Try to get cached writer
	writerMutex.RLock()
	writer, exists := writerCache[ctx]
	writerMutex.RUnlock()

	if exists {
		return writer, nil
	}

	// Create new writer
	writerMutex.Lock()
	defer writerMutex.Unlock()

	// Double-check after acquiring write lock
	if writer, exists := writerCache[ctx]; exists {
		return writer, nil
	}

	// Create writer based on context.Accept
	writer, err := createWriter(ctx)
	if err != nil {
		return nil, err
	}

	// Cache the writer
	writerCache[ctx] = writer

	return writer, nil
}

// createWriter creates a writer based on context.Accept
func createWriter(ctx *context.Context) (message.Writer, error) {
	// If global factory is set, use it
	if globalFactory != nil {
		return globalFactory.NewWriter(ctx, nil)
	}

	// Default: create based on Accept type
	switch ctx.Accept {
	case context.AcceptStandard:
		// OpenAI-compatible format
		return openai.NewWriter(ctx)

	case context.AcceptWebCUI, context.AccepNativeCUI, context.AcceptDesktopCUI:
		// CUI format
		return cui.NewWriter(ctx)

	default:
		// Default to Standard
		return openai.NewWriter(ctx)
	}
}

// SetWriterFactory sets a custom writer factory
// This allows applications to provide their own writer implementations
func SetWriterFactory(factory message.WriterFactory) {
	globalFactory = factory
}

// ClearWriterCache clears the writer cache
// Should be called when contexts are cleaned up
func ClearWriterCache(ctx *context.Context) {
	writerMutex.Lock()
	defer writerMutex.Unlock()
	delete(writerCache, ctx)
}

// ClearAllWriterCache clears all cached writers
func ClearAllWriterCache() {
	writerMutex.Lock()
	defer writerMutex.Unlock()
	writerCache = make(map[*context.Context]message.Writer)
}

// Flush flushes the writer for the given context
func Flush(ctx *context.Context) error {
	writer, err := GetWriter(ctx)
	if err != nil {
		return err
	}
	return writer.Flush()
}

// Close closes the writer for the given context and removes it from cache
func Close(ctx *context.Context) error {
	writer, err := GetWriter(ctx)
	if err != nil {
		return err
	}

	err = writer.Close()
	ClearWriterCache(ctx)
	return err
}

// SendMulti is a convenience function to send multiple messages
func SendMulti(ctx *context.Context, messages ...*message.Message) error {
	writer, err := GetWriter(ctx)
	if err != nil {
		return err
	}

	for _, msg := range messages {
		if err := writer.Write(msg); err != nil {
			return fmt.Errorf("failed to send message: %w", err)
		}
	}

	return nil
}
