package vm

import (
	ffi "github.com/filecoin-project/filecoin-ffi"
	"github.com/filecoin-project/go-address"
	"github.com/filecoin-project/go-sectorbuilder"
	"github.com/filecoin-project/specs-actors/actors/abi"
	"github.com/filecoin-project/specs-actors/actors/crypto"
	"github.com/filecoin-project/specs-actors/actors/runtime"
	"github.com/ipfs/go-cid"
	mh "github.com/multiformats/go-multihash"
	"golang.org/x/xerrors"
)

func init() {
	mh.Codes[0xf104] = "filecoin"
}

// Actual type is defined in chain/types/vmcontext.go because the VMContext interface is there

func Syscalls(verifier sectorbuilder.Verifier) runtime.Syscalls {
	return &syscallShim{verifier}
}

type syscallShim struct {
	verifier sectorbuilder.Verifier
}

func (ss *syscallShim) ComputeUnsealedSectorCID(ssize abi.SectorSize, pieces []abi.PieceInfo) (cid.Cid, error) {
	// TODO: does this pull in unwanted dependencies?
	var ffipieces []ffi.PublicPieceInfo
	for _, p := range pieces {
		ffipieces = append(ffipieces, ffi.PublicPieceInfo{
			Size:  p.Size.Unpadded(),
			CommP: cidToCommD(p.PieceCID),
		})
	}

	commd, err := sectorbuilder.GenerateDataCommitment(ssize, ffipieces)
	if err != nil {
		log.Errorf("generate data commitment failed: %s", err)
		return cid.Undef, err
	}

	// TODO: pulling these numbers kinda out of an unmerged PR
	hb, err := mh.Encode(commd[:], 0xf104)
	if err != nil {
		log.Errorf("mh encode failed: %s", err)
		return cid.Undef, err
	}

	return cid.NewCidV1(0xf101, mh.Multihash(hb)), nil
}

func (ss *syscallShim) HashBlake2b(data []byte) [32]byte {
	panic("NYI")
}

func (ss *syscallShim) VerifyConsensusFault(a, b []byte) bool {
	panic("NYI")
}

func (ss *syscallShim) VerifyPoSt(ssize abi.SectorSize, proof abi.PoStVerifyInfo) (bool, error) {
	panic("NYI")
}

func cidToCommD(c cid.Cid) [32]byte {
	b := c.Bytes()
	var out [32]byte
	copy(out[:], b[len(b)-32:])
	return out
}

func cidToCommR(c cid.Cid) [32]byte {
	b := c.Bytes()
	var out [32]byte
	copy(out[:], b[len(b)-32:])
	return out
}

func (ss *syscallShim) VerifySeal(ssize abi.SectorSize, info abi.SealVerifyInfo) (bool, error) {
	//_, span := trace.StartSpan(ctx, "ValidatePoRep")
	//defer span.End()

	commD := cidToCommD(info.UnsealedCID)
	commR := cidToCommR(info.OnChain.SealedCID)

	miner, err := address.NewIDAddress(uint64(info.Miner))
	if err != nil {
		return false, xerrors.Errorf("weirdly failed to construct address: %w", err)
	}

	ticket := []byte(info.Randomness)
	proof := []byte(info.OnChain.Proof)
	seed := []byte(info.InteractiveRandomness)

	//func(ctx context.Context, maddr address.Address, ssize abi.SectorSize, commD, commR, ticket, proof, seed []byte, sectorID abi.SectorNumber)
	ok, err := ss.verifier.VerifySeal(ssize, commR[:], commD[:], miner, ticket, seed, info.SectorID.Number, proof)
	if err != nil {
		return false, xerrors.Errorf("failed to validate PoRep: %w", err)
	}

	return ok, nil
}

func (ss *syscallShim) VerifySignature(sig crypto.Signature, addr address.Address, input []byte) bool {
	return true
	/* // TODO: in genesis setup, we are currently faking signatures
	if err := ss.rt.vmctx.VerifySignature(&sig, addr, input); err != nil {
		return false
	}
	return true
	*/
}