package oci

import (
	"bytes"
	"context"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"

	"github.com/docker/docker/pkg/idtools"
	"github.com/moby/buildkit/executor"
	"github.com/moby/buildkit/identity"
)

const hostsContent = `
127.0.0.1	localhost buildkitsandbox
::1	localhost ip6-localhost ip6-loopback
`

func GetHostsFile(ctx context.Context, stateDir string, extraHosts []executor.HostIP, idmap *idtools.IdentityMapping) (string, func(), error) {
	if len(extraHosts) == 0 {
		_, err := g.Do(ctx, stateDir, func(ctx context.Context) (interface{}, error) {
			_, _, err := makeHostsFile(stateDir, nil, idmap)
			return nil, err
		})
		if err != nil {
			return "", nil, err
		}
		return filepath.Join(stateDir, "hosts"), func() {}, nil
	}
	return makeHostsFile(stateDir, extraHosts, idmap)
}

func makeHostsFile(stateDir string, extraHosts []executor.HostIP, idmap *idtools.IdentityMapping) (string, func(), error) {
	p := filepath.Join(stateDir, "hosts")
	if len(extraHosts) != 0 {
		p += "." + identity.NewID()
	}
	_, err := os.Stat(p)
	if err == nil {
		return "", func() {}, nil
	}
	if !os.IsNotExist(err) {
		return "", nil, err
	}

	b := &bytes.Buffer{}

	if _, err := b.Write([]byte(hostsContent)); err != nil {
		return "", nil, err
	}

	for _, h := range extraHosts {
		if _, err := b.Write([]byte(fmt.Sprintf("%s\t%s\n", h.IP.String(), h.Host))); err != nil {
			return "", nil, err
		}
	}

	tmpPath := p + ".tmp"
	if err := ioutil.WriteFile(tmpPath, b.Bytes(), 0644); err != nil {
		return "", nil, err
	}

	if idmap != nil {
		root := idmap.RootPair()
		if err := os.Chown(tmpPath, root.UID, root.GID); err != nil {
			return "", nil, err
		}
	}

	if err := os.Rename(tmpPath, p); err != nil {
		return "", nil, err
	}
	return p, func() {
		os.RemoveAll(p)
	}, nil
}