package automerge

/*
#include "automerge.h"

#cgo LDFLAGS: -L${SRCDIR}/deps
#cgo darwin,arm64 LDFLAGS: -lautomerge_core_darwin_arm64
#cgo darwin,amd64 LDFLAGS: -lautomerge_core_darwin_amd64
#cgo linux,arm64 LDFLAGS: -lautomerge_core_linux_arm64 -lm
#cgo linux,amd64 LDFLAGS: -lautomerge_core_linux_amd64 -lm
*/
import "C"
import (
	"fmt"
	"runtime"

	_ "github.com/automerge/automerge-go/deps"
)

// result wraps an AMresult, and arranges for it to be AMfree'd after
// the result is garbage collected.
type result struct {
	cResult *C.AMresult
}

func wrap(r *C.AMresult) *result {
	ret := &result{r}
	runtime.SetFinalizer(ret, func(*result) { C.AMresultFree(r) })
	return ret
}

func (r *result) void() error {
	item, err := r.item()
	if err != nil {
		return err
	}
	if err == nil && item.Kind() != KindVoid {
		return fmt.Errorf("expected KindVoid, got: %s", item.Kind())
	}
	return nil
}

func (r *result) item() (*item, error) {
	items, err := r.items()
	if err != nil {
		return nil, err
	}
	if len(items) != 1 {
		return nil, fmt.Errorf("automerge: expected single return value, got %v", len(items))
	}
	return items[0], nil
}

func (r *result) items() ([]*item, error) {
	defer runtime.KeepAlive(r)

	switch C.AMresultStatus(r.cResult) {
	case C.AM_STATUS_OK:
		items := C.AMresultItems(r.cResult)
		ret := []*item{}
		for {
			i := C.AMitemsNext(&items, 1)
			if i == nil {
				break
			}

			ret = append(ret, &item{result: r, cItem: i})
		}
		return ret, nil
	case C.AM_STATUS_ERROR:
		msg := fromByteSpanStr(C.AMresultError(r.cResult))
		return nil, fmt.Errorf(msg)
	case C.AM_STATUS_INVALID_RESULT:
		return nil, fmt.Errorf("automerge: invalid result")
	default:
		return nil, fmt.Errorf("automerge: invalid result status")
	}
}

func must[T any](r T, e error) T {
	if e != nil {
		panic(e)
	}
	return r
}