package ugh

import (
	"context"
	"fmt"
	"io"

	"github.com/charmbracelet/x/vt"
)

func (c *Console) initEmulator(appOutput io.Reader, appInput io.Writer) {
	emu := vt.NewEmulator(c.width, c.height)

	c.mu.Lock()
	c.emu = emu
	c.mu.Unlock()

	go func() {
		buf := make([]byte, 4096)
		for {
			n, err := emu.Read(buf)
			if n > 0 {
				_, werr := appInput.Write(buf[:n])
				if err == nil {
					err = werr
				}
			}
			if err != nil {
				return
			}
		}
	}()

	go func() {
		buf := make([]byte, 4096)
		for {
			n, err := appOutput.Read(buf)
			if n > 0 {
				c.mu.Lock()
				_, werr := emu.Write(buf[:n])
				if err == nil {
					err = werr
				}
				c.mu.Unlock()
			}
			if err != nil {
				return
			}
		}
	}()
}

// Start executes all recorded steps asynchronously in a background goroutine.
// It returns a wait function that blocks until all steps complete and calls
// t.Fatalf on the caller's goroutine if any step failed.
func (c *Console) Start(ctx context.Context, appOutput io.Reader, appInput io.Writer) func() {
	c.t.Helper()
	c.started = true
	c.initEmulator(appOutput, appInput)

	var stepErr error
	done := make(chan struct{})

	go func() {
		defer close(done)
		for _, s := range c.steps {
			if err := s.execute(ctx, c, appInput); err != nil {
				stepErr = fmt.Errorf("ugh: step %s failed: %w", s, err)
				return
			}
			c.executed++
		}
	}()

	return func() {
		<-done
		if stepErr != nil {
			c.t.Helper()
			c.t.Fatalf("%s", stepErr)
		}
	}
}

// Run executes all recorded steps synchronously against the application's
// I/O streams. Calls t.Fatalf on failure.
func (c *Console) Run(ctx context.Context, appOutput io.Reader, appInput io.Writer) {
	c.t.Helper()
	wait := c.Start(ctx, appOutput, appInput)
	wait()
}
