package main

import (
	"bytes"
	"encoding/json"
	"errors"
	"io"
	"log"
	"os"
	"os/exec"
)

var (
	needsSudo bool
)

// dockerImage describes an image available in the docker daemon.
type dockerImage struct {
	ID         string
	Repository string
	Tag        string
}

// dockerCommand creates an *exec.Cmd for running docker. It respects the global
// `needsSudo` state.
func dockerCommand(args ...string) *exec.Cmd {
	docker := os.Getenv("DOCKER")
	if docker == "" {
		docker = "docker"
	}

	if needsSudo {
		useArgs := make([]string, 0, len(args)+1)
		useArgs = append(useArgs, docker)
		useArgs = append(useArgs, args...)
		return exec.Command(`sudo`, useArgs...)
	}

	return exec.Command(docker, args...)
}

// dockerListImages lists all the current docker images.
func dockerListImages() ([]dockerImage, error) {

	cmd := dockerCommand(`image`, `ls`, `--format`, `{{json . }}`)
	cmd.Stderr = os.Stderr // passthrough

	buff, err := cmd.Output()
	if err != nil {
		if !needsSudo {
			// Retry with sudo
			log.Println("Retrying with sudo...")
			needsSudo = true
			return dockerListImages()
		}

		return nil, err
	}

	var ret []dockerImage

	dec := json.NewDecoder(bytes.NewReader(buff))
	for {
		var entry dockerImage
		err = dec.Decode(&entry)
		if err != nil {
			if errors.Is(err, io.EOF) {
				return ret, nil
			}
			return nil, err // real error
		}

		ret = append(ret, entry)
	}
}

// dockerFindImage searches all the current docker images to find one named as
// the supplied `repository:tag`.
func dockerFindImage(repository, tag string) (*dockerImage, error) {

	images, err := dockerListImages()
	if err != nil {
		return nil, err
	}

	for _, im := range images {
		if im.Repository == repository && im.Tag == tag {
			// found it
			return &im, nil
		}
	}

	// No match
	return nil, os.ErrNotExist
}

// dockerBuild builds the supplied dockerfile and tags it as repository:tag
// as well as repository:latest.
func dockerBuild(dockerfile []byte, repository, tag string) error {
	cmd := dockerCommand(`build`, `-t`, repository+`:`+tag, `-t`, repository+`:latest`, `-`)
	cmd.Stderr = os.Stderr
	cmd.Stdin = bytes.NewReader(dockerfile)

	return cmd.Run()
}