package pty

import (
	"bytes"
	"context"
	"io"
	"sync"
	"sync/atomic"
)

// OutputRouter manages buffered vs live output.
type OutputRouter struct {
	mu        sync.Mutex
	stdout    io.Writer
	buffer    bytes.Buffer
	buffering bool
}

func NewOutputRouter(out io.Writer) (*OutputRouter, error) {
	return &OutputRouter{
		stdout: out,
	}, nil
}

func (r *OutputRouter) Write(p []byte) (n int, err error) {
	r.mu.Lock()
	defer r.mu.Unlock()

	if r.buffering {
		// We are in "Prompt Mode", so save this output for later.
		// If we printed it now, it would mess up the confirmation prompt.
		return r.buffer.Write(p)
	}

	// Normal mode: just print it to stdout.
	return r.stdout.Write(p)
}

// Pause starts buffering output. Call this before showing a confirmation prompt.
func (r *OutputRouter) Pause() {
	r.mu.Lock()
	defer r.mu.Unlock()
	r.buffering = true
}

// Resume stops buffering, flushes any buffered output, and resumes live output.
// Call this after the confirmation prompt is complete.
func (r *OutputRouter) Resume() {
	r.mu.Lock()
	defer r.mu.Unlock()

	// Flush any buffered output
	if r.buffer.Len() > 0 {
		_, _ = io.Copy(r.stdout, &r.buffer)
		r.buffer.Reset()
	}

	r.buffering = false
}

// writerDest wraps io.Writer for use with atomic.Pointer
// (atomic.Value panics on nil interface stores)
type writerDest struct {
	w io.Writer
}

// InputRouter manages routing stdin to either PTY or a prompt pipe.
// Only ONE goroutine should call ReadLoop().
type InputRouter struct {
	dest       atomic.Pointer[writerDest]
	defaultDst io.Writer // PTY writer
}

func NewInputRouter(ptyWriter io.Writer) (*InputRouter, error) {
	return &InputRouter{
		defaultDst: ptyWriter,
	}, nil
}

// ReadLoop continuously reads from src and routes data to the current destination.
//
// IMPORTANT: Only ONE goroutine should call ReadLoop() because:
//  1. Multiple readers on the same source (e.g., stdin) cause data splitting -
//     one goroutine might read "hel" while another reads "lo\n"
//  2. Concurrent routing decisions create race conditions on the destination
//  3. User input becomes unpredictably interleaved between readers
//
// This function blocks until src returns an error (e.g., EOF).
func (r *InputRouter) ReadLoop(src io.Reader) {
	r.ReadLoopContext(context.Background(), src)
}

// ReadLoopContext continuously reads from src and routes data to the current
// destination until src returns an error or ctx is cancelled.
func (r *InputRouter) ReadLoopContext(ctx context.Context, src io.Reader) {
	buf := make([]byte, 1024)
	for {
		nr, err := readInput(ctx, src, buf)
		if err != nil {
			return
		}

		if nr == 0 {
			continue
		}

		if dest := r.dest.Load(); dest != nil {
			if _, err := dest.w.Write(buf[:nr]); err != nil {
				return
			}
		} else {
			if _, err := r.defaultDst.Write(buf[:nr]); err != nil {
				return
			}
		}
	}
}

// RouteToPrompt switches input to go to the given writer (prompt pipe)
func (r *InputRouter) RouteToPrompt(w io.Writer) {
	r.dest.Store(&writerDest{w: w})
}

// RouteToPTY switches input back to the PTY (default)
func (r *InputRouter) RouteToPTY() {
	r.dest.Store(nil)
}
