package integration

import (
	"fmt"
	"os"
	"path/filepath"
	"strings"

	. "github.com/containers/libpod/test/utils"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

var _ = Describe("Podman push", func() {
	var (
		tempdir    string
		err        error
		podmanTest *PodmanTestIntegration
	)

	BeforeEach(func() {
		tempdir, err = CreateTempDirInTempDir()
		if err != nil {
			os.Exit(1)
		}
		podmanTest = PodmanTestCreate(tempdir)
		podmanTest.RestoreAllArtifacts()
	})

	AfterEach(func() {
		podmanTest.Cleanup()
		f := CurrentGinkgoTestDescription()
		timedResult := fmt.Sprintf("Test: %s completed in %f seconds", f.TestText, f.Duration.Seconds())
		GinkgoWriter.Write([]byte(timedResult))
	})

	It("podman push to containers/storage", func() {
		session := podmanTest.Podman([]string{"push", ALPINE, "containers-storage:busybox:test"})
		session.WaitWithDefaultTimeout()
		Expect(session.ExitCode()).To(Equal(0))

		session = podmanTest.Podman([]string{"rmi", ALPINE})
		session.WaitWithDefaultTimeout()
		Expect(session.ExitCode()).To(Equal(0))

		session = podmanTest.Podman([]string{"rmi", "busybox:test"})
		session.WaitWithDefaultTimeout()
		Expect(session.ExitCode()).To(Equal(0))
	})

	It("podman push to dir", func() {
		session := podmanTest.Podman([]string{"push", "--remove-signatures", ALPINE, "dir:/tmp/busybox"})
		session.WaitWithDefaultTimeout()
		Expect(session.ExitCode()).To(Equal(0))

		clean := SystemExec("rm", []string{"-fr", "/tmp/busybox"})
		clean.WaitWithDefaultTimeout()
		Expect(clean.ExitCode()).To(Equal(0))
	})

	It("podman push to local registry", func() {
		if podmanTest.Host.Arch == "ppc64le" {
			Skip("No registry image for ppc64le")
		}
		podmanTest.RestoreArtifact(registry)
		session := podmanTest.Podman([]string{"run", "-d", "--name", "registry", "-p", "5000:5000", registry, "/entrypoint.sh", "/etc/docker/registry/config.yml"})
		session.WaitWithDefaultTimeout()
		Expect(session.ExitCode()).To(Equal(0))

		if !WaitContainerReady(podmanTest, "registry", "listening on", 20, 1) {
			Skip("Can not start docker registry.")
		}

		push := podmanTest.Podman([]string{"push", "--tls-verify=false", "--remove-signatures", ALPINE, "localhost:5000/my-alpine"})
		push.WaitWithDefaultTimeout()
		Expect(push.ExitCode()).To(Equal(0))
	})

	It("podman push to local registry with authorization", func() {
		if podmanTest.Host.Arch == "ppc64le" {
			Skip("No registry image for ppc64le")
		}
		authPath := filepath.Join(podmanTest.TempDir, "auth")
		os.Mkdir(authPath, os.ModePerm)
		os.MkdirAll("/etc/containers/certs.d/localhost:5000", os.ModePerm)
		debug := SystemExec("ls", []string{"-l", podmanTest.TempDir})
		debug.WaitWithDefaultTimeout()

		cwd, _ := os.Getwd()
		certPath := filepath.Join(cwd, "../", "certs")

		if IsCommandAvailable("getenforce") {
			ge := SystemExec("getenforce", []string{})
			ge.WaitWithDefaultTimeout()
			if ge.OutputToString() == "Enforcing" {
				se := SystemExec("setenforce", []string{"0"})
				se.WaitWithDefaultTimeout()

				defer SystemExec("setenforce", []string{"1"})
			}
		}
		podmanTest.RestoreArtifact(registry)
		session := podmanTest.Podman([]string{"run", "--entrypoint", "htpasswd", registry, "-Bbn", "podmantest", "test"})
		session.WaitWithDefaultTimeout()
		Expect(session.ExitCode()).To(Equal(0))

		f, _ := os.Create(filepath.Join(authPath, "htpasswd"))
		defer f.Close()

		f.WriteString(session.OutputToString())
		f.Sync()
		debug = SystemExec("cat", []string{filepath.Join(authPath, "htpasswd")})
		debug.WaitWithDefaultTimeout()

		session = podmanTest.Podman([]string{"run", "-d", "-p", "5000:5000", "--name", "registry", "-v",
			strings.Join([]string{authPath, "/auth"}, ":"), "-e", "REGISTRY_AUTH=htpasswd", "-e",
			"REGISTRY_AUTH_HTPASSWD_REALM=Registry Realm", "-e", "REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd",
			"-v", strings.Join([]string{certPath, "/certs"}, ":"), "-e", "REGISTRY_HTTP_TLS_CERTIFICATE=/certs/domain.crt",
			"-e", "REGISTRY_HTTP_TLS_KEY=/certs/domain.key", registry})
		session.WaitWithDefaultTimeout()
		Expect(session.ExitCode()).To(Equal(0))

		if !WaitContainerReady(podmanTest, "registry", "listening on", 20, 1) {
			Skip("Can not start docker registry.")
		}

		session = podmanTest.Podman([]string{"logs", "registry"})
		session.WaitWithDefaultTimeout()

		push := podmanTest.Podman([]string{"push", "--creds=podmantest:test", ALPINE, "localhost:5000/tlstest"})
		push.WaitWithDefaultTimeout()
		Expect(push.ExitCode()).To(Not(Equal(0)))

		push = podmanTest.Podman([]string{"push", "--creds=podmantest:test", "--tls-verify=false", ALPINE, "localhost:5000/tlstest"})
		push.WaitWithDefaultTimeout()
		Expect(push.ExitCode()).To(Equal(0))

		setup := SystemExec("cp", []string{filepath.Join(certPath, "domain.crt"), "/etc/containers/certs.d/localhost:5000/ca.crt"})
		setup.WaitWithDefaultTimeout()
		defer os.RemoveAll("/etc/containers/certs.d/localhost:5000")

		push = podmanTest.Podman([]string{"push", "--creds=podmantest:wrongpasswd", ALPINE, "localhost:5000/credstest"})
		push.WaitWithDefaultTimeout()
		Expect(push.ExitCode()).To(Not(Equal(0)))

		push = podmanTest.Podman([]string{"push", "--creds=podmantest:test", "--cert-dir=fakedir", ALPINE, "localhost:5000/certdirtest"})
		push.WaitWithDefaultTimeout()
		Expect(push.ExitCode()).To(Not(Equal(0)))

		push = podmanTest.Podman([]string{"push", "--creds=podmantest:test", ALPINE, "localhost:5000/defaultflags"})
		push.WaitWithDefaultTimeout()
		Expect(push.ExitCode()).To(Equal(0))
	})

	It("podman push to docker-archive", func() {
		session := podmanTest.Podman([]string{"push", ALPINE, "docker-archive:/tmp/alp:latest"})
		session.WaitWithDefaultTimeout()
		Expect(session.ExitCode()).To(Equal(0))
		clean := SystemExec("rm", []string{"/tmp/alp"})
		clean.WaitWithDefaultTimeout()
		Expect(clean.ExitCode()).To(Equal(0))
	})

	It("podman push to docker daemon", func() {
		setup := SystemExec("bash", []string{"-c", "systemctl status docker 2>&1"})
		setup.WaitWithDefaultTimeout()

		if setup.LineInOutputContains("Active: inactive") {
			setup = SystemExec("systemctl", []string{"start", "docker"})
			setup.WaitWithDefaultTimeout()

			defer SystemExec("systemctl", []string{"stop", "docker"})
		} else if setup.ExitCode() != 0 {
			Skip("Docker is not available")
		}

		session := podmanTest.Podman([]string{"push", ALPINE, "docker-daemon:alpine:podmantest"})
		session.WaitWithDefaultTimeout()
		Expect(session.ExitCode()).To(Equal(0))

		check := SystemExec("docker", []string{"images", "--format", "{{.Repository}}:{{.Tag}}"})
		check.WaitWithDefaultTimeout()
		Expect(check.ExitCode()).To(Equal(0))
		Expect(check.OutputToString()).To(ContainSubstring("alpine:podmantest"))

		clean := SystemExec("docker", []string{"rmi", "alpine:podmantest"})
		clean.WaitWithDefaultTimeout()
		Expect(clean.ExitCode()).To(Equal(0))
	})

	It("podman push to oci-archive", func() {
		session := podmanTest.Podman([]string{"push", ALPINE, "oci-archive:/tmp/alp.tar:latest"})
		session.WaitWithDefaultTimeout()
		Expect(session.ExitCode()).To(Equal(0))
		clean := SystemExec("rm", []string{"/tmp/alp.tar"})
		clean.WaitWithDefaultTimeout()
		Expect(clean.ExitCode()).To(Equal(0))
	})

	It("podman push to local ostree", func() {
		if !IsCommandAvailable("ostree") {
			Skip("ostree is not installed")
		}

		ostreePath := filepath.Join(podmanTest.TempDir, "ostree/repo")
		os.MkdirAll(ostreePath, os.ModePerm)

		setup := SystemExec("ostree", []string{strings.Join([]string{"--repo=", ostreePath}, ""), "init"})
		setup.WaitWithDefaultTimeout()

		session := podmanTest.Podman([]string{"push", ALPINE, strings.Join([]string{"ostree:alp@", ostreePath}, "")})
		session.WaitWithDefaultTimeout()
		Expect(session.ExitCode()).To(Equal(0))

		clean := SystemExec("rm", []string{"-rf", ostreePath})
		clean.WaitWithDefaultTimeout()
		Expect(clean.ExitCode()).To(Equal(0))
	})

	It("podman push to docker-archive no reference", func() {
		session := podmanTest.Podman([]string{"push", ALPINE, "docker-archive:/tmp/alp"})
		session.WaitWithDefaultTimeout()
		Expect(session.ExitCode()).To(Equal(0))
		clean := SystemExec("rm", []string{"/tmp/alp"})
		clean.WaitWithDefaultTimeout()
		Expect(clean.ExitCode()).To(Equal(0))
	})

	It("podman push to oci-archive no reference", func() {
		session := podmanTest.Podman([]string{"push", ALPINE, "oci-archive:/tmp/alp-oci"})
		session.WaitWithDefaultTimeout()
		Expect(session.ExitCode()).To(Equal(0))
		clean := SystemExec("rm", []string{"/tmp/alp-oci"})
		clean.WaitWithDefaultTimeout()
		Expect(clean.ExitCode()).To(Equal(0))
	})

})