package readablestream

import (
	"context"
	"io"

	"github.com/nlepage/go-wasm-http-server/v2/internal/jstype"
	"github.com/nlepage/go-wasm-http-server/v2/internal/safejs"
)

type Writer struct {
	Value      safejs.Value
	controller safejs.Value
	ctx        context.Context
}

var _ io.WriteCloser = (*Writer)(nil)

func NewWriter() (*Writer, error) {
	var start safejs.Func
	var controller safejs.Value

	start, err := safejs.FuncOf(func(_ safejs.Value, args []safejs.Value) any {
		defer start.Release()
		controller = args[0]
		return nil
	})
	if err != nil {
		return nil, err
	}

	var cancel safejs.Func
	ctx, cancelCtx := context.WithCancel(context.Background())

	cancel, err = safejs.FuncOf(func(_ safejs.Value, _ []safejs.Value) any {
		defer cancel.Release()
		cancelCtx()
		return nil
	})
	if err != nil {
		return nil, err
	}

	source, err := safejs.ValueOf(map[string]any{
		"start":  safejs.Unsafe(start.Value()),
		"cancel": safejs.Unsafe(cancel.Value()),
	})
	if err != nil {
		return nil, err
	}

	value, err := jstype.ReadableStream.New(source)
	if err != nil {
		return nil, err
	}

	return &Writer{
		Value:      value,
		controller: controller,
		ctx:        ctx,
	}, nil
}

func (rs *Writer) Write(b []byte) (int, error) {
	chunk, err := jstype.Uint8Array.New(len(b)) // FIXME reuse same Uint8Array?
	if err != nil {
		return 0, err
	}

	n, err := safejs.CopyBytesToJS(chunk, b)
	if err != nil {
		return 0, err
	}

	_, err = rs.controller.Call("enqueue", chunk)

	return n, err
}

func (rs *Writer) Close() error {
	rs.controller.Call("close")
	return nil
}

func (rs *Writer) Context() context.Context {
	return rs.ctx
}
