package bindings

import (
	"syscall/js"

	"github.com/fudanchii/infr"
)

type Promise[T infr.TryFromType[js.Value, T]] struct {
	js.Value
}

func (p Promise[T]) forward(t string, f func(arg js.Value)) Promise[T] {
	var cb js.Func
	cb = js.FuncOf(func(_ js.Value, args []js.Value) any {
		f(args[0])
		cb.Release()
		return nil
	})

	p.Call(t, cb)

	return p
}

func (p Promise[T]) Catch(f func(arg js.Value)) Promise[T] {
	return p.forward("catch", f)
}

func (p Promise[T]) Then(f func(arg js.Value)) Promise[T] {
	return p.forward("then", f)
}

func (p Promise[T]) Await() (T, error) {
	vchan := make(chan js.Value, 1)
	echan := make(chan js.Value, 1)

	p.Then(func(arg js.Value) { vchan <- arg }).
		Catch(func(err js.Value) { echan <- err })

	var defT T
	select {
	case err := <-echan:
		return defT, PromiseError{err}
	case v := <-vchan:
		return infr.TryInto[T](v)
	}
}

type PromiseError struct {
	js.Value
}

func (pe PromiseError) Error() string {
	if pe.Get("message").Type() == js.TypeString {
		return pe.Get("message").String()
	}

	return pe.String()
}