// +build !windows

package buildkit

import (
	"fmt"
	"path/filepath"
	"sync"

	"github.com/docker/libnetwork"
	"github.com/moby/buildkit/executor"
	"github.com/moby/buildkit/executor/runcexecutor"
	"github.com/moby/buildkit/identity"
	"github.com/moby/buildkit/util/network"
	"github.com/pkg/errors"
	"github.com/sirupsen/logrus"
)

const networkName = "bridge"

func newExecutor(root string, net libnetwork.NetworkController) (executor.Executor, error) {
	// FIXME: fix bridge networking
	_ = bridgeProvider{}
	return runcexecutor.New(runcexecutor.Opt{
		Root:              filepath.Join(root, "executor"),
		CommandCandidates: []string{"docker-runc", "runc"},
	}, nil)
}

type bridgeProvider struct {
	libnetwork.NetworkController
}

func (p *bridgeProvider) NewInterface() (network.Interface, error) {
	n, err := p.NetworkByName(networkName)
	if err != nil {
		return nil, err
	}

	iface := &lnInterface{ready: make(chan struct{})}
	iface.Once.Do(func() {
		go iface.init(p.NetworkController, n)
	})

	return iface, nil
}

func (p *bridgeProvider) Release(iface network.Interface) error {
	go func() {
		if err := p.release(iface); err != nil {
			logrus.Errorf("%s", err)
		}
	}()
	return nil
}

func (p *bridgeProvider) release(iface network.Interface) error {
	li, ok := iface.(*lnInterface)
	if !ok {
		return errors.Errorf("invalid interface %T", iface)
	}
	err := li.sbx.Delete()
	if err1 := li.ep.Delete(true); err1 != nil && err == nil {
		err = err1
	}
	return err
}

type lnInterface struct {
	ep  libnetwork.Endpoint
	sbx libnetwork.Sandbox
	sync.Once
	err   error
	ready chan struct{}
}

func (iface *lnInterface) init(c libnetwork.NetworkController, n libnetwork.Network) {
	defer close(iface.ready)
	id := identity.NewID()

	ep, err := n.CreateEndpoint(id)
	if err != nil {
		iface.err = err
		return
	}

	sbx, err := c.NewSandbox(id)
	if err != nil {
		iface.err = err
		return
	}

	if err := ep.Join(sbx); err != nil {
		iface.err = err
		return
	}

	iface.sbx = sbx
	iface.ep = ep
}

func (iface *lnInterface) Set(pid int) error {
	<-iface.ready
	if iface.err != nil {
		return iface.err
	}
	return iface.sbx.SetKey(fmt.Sprintf("/proc/%d/ns/net", pid))
}

func (iface *lnInterface) Remove(pid int) error {
	return nil
}