package libpod

import (
	"encoding/json"
	"net/http"

	"github.com/containers/buildah/manifests"
	copy2 "github.com/containers/image/v5/copy"
	"github.com/containers/image/v5/manifest"
	"github.com/containers/image/v5/transports/alltransports"
	"github.com/containers/podman/v2/libpod"
	"github.com/containers/podman/v2/libpod/image"
	"github.com/containers/podman/v2/pkg/api/handlers"
	"github.com/containers/podman/v2/pkg/api/handlers/utils"
	"github.com/containers/podman/v2/pkg/domain/infra/abi"
	"github.com/gorilla/schema"
	"github.com/opencontainers/go-digest"
	"github.com/pkg/errors"
)

func ManifestCreate(w http.ResponseWriter, r *http.Request) {
	runtime := r.Context().Value("runtime").(*libpod.Runtime)
	decoder := r.Context().Value("decoder").(*schema.Decoder)
	query := struct {
		Name  []string `schema:"name"`
		Image []string `schema:"image"`
		All   bool     `schema:"all"`
	}{
		// Add defaults here once needed.
	}
	if err := decoder.Decode(&query, r.URL.Query()); err != nil {
		utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest,
			errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String()))
		return
	}
	rtc, err := runtime.GetConfig()
	if err != nil {
		utils.InternalServerError(w, err)
		return
	}
	sc := image.GetSystemContext(rtc.Engine.SignaturePolicyPath, "", false)
	manID, err := image.CreateManifestList(runtime.ImageRuntime(), *sc, query.Name, query.Image, query.All)
	if err != nil {
		utils.InternalServerError(w, err)
		return
	}
	utils.WriteResponse(w, http.StatusOK, handlers.IDResponse{ID: manID})
}

func ManifestInspect(w http.ResponseWriter, r *http.Request) {
	runtime := r.Context().Value("runtime").(*libpod.Runtime)
	name := utils.GetName(r)
	imageEngine := abi.ImageEngine{Libpod: runtime}
	inspectReport, inspectError := imageEngine.ManifestInspect(r.Context(), name)
	if inspectError != nil {
		utils.Error(w, "Something went wrong.", http.StatusNotFound, inspectError)
		return
	}
	var list manifest.Schema2List
	if err := json.Unmarshal(inspectReport, &list); err != nil {
		utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "Unmarshal()"))
		return
	}
	utils.WriteResponse(w, http.StatusOK, &list)
}

func ManifestAdd(w http.ResponseWriter, r *http.Request) {
	runtime := r.Context().Value("runtime").(*libpod.Runtime)
	var manifestInput image.ManifestAddOpts
	if err := json.NewDecoder(r.Body).Decode(&manifestInput); err != nil {
		utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "Decode()"))
		return
	}
	name := utils.GetName(r)
	newImage, err := runtime.ImageRuntime().NewFromLocal(name)
	if err != nil {
		utils.ImageNotFound(w, name, err)
		return
	}
	rtc, err := runtime.GetConfig()
	if err != nil {
		utils.InternalServerError(w, err)
		return
	}
	sc := image.GetSystemContext(rtc.Engine.SignaturePolicyPath, "", false)
	newID, err := newImage.AddManifest(*sc, manifestInput)
	if err != nil {
		utils.InternalServerError(w, err)
		return
	}
	utils.WriteResponse(w, http.StatusOK, handlers.IDResponse{ID: newID})
}

func ManifestRemove(w http.ResponseWriter, r *http.Request) {
	runtime := r.Context().Value("runtime").(*libpod.Runtime)
	decoder := r.Context().Value("decoder").(*schema.Decoder)
	query := struct {
		Digest string `schema:"digest"`
	}{
		// Add defaults here once needed.
	}
	name := utils.GetName(r)
	if err := decoder.Decode(&query, r.URL.Query()); err != nil {
		utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest,
			errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String()))
		return
	}
	newImage, err := runtime.ImageRuntime().NewFromLocal(name)
	if err != nil {
		utils.ImageNotFound(w, name, err)
		return
	}
	d, err := digest.Parse(query.Digest)
	if err != nil {
		utils.Error(w, "invalid digest", http.StatusBadRequest, err)
		return
	}
	newID, err := newImage.RemoveManifest(d)
	if err != nil {
		utils.InternalServerError(w, err)
		return
	}
	utils.WriteResponse(w, http.StatusOK, handlers.IDResponse{ID: newID})
}
func ManifestPush(w http.ResponseWriter, r *http.Request) {
	// FIXME: parameters are missing (tlsVerify, format).
	// Also, we should use the ABI function to avoid duplicate code.
	// Also, support for XRegistryAuth headers are missing.

	runtime := r.Context().Value("runtime").(*libpod.Runtime)
	decoder := r.Context().Value("decoder").(*schema.Decoder)
	query := struct {
		All         bool   `schema:"all"`
		Destination string `schema:"destination"`
	}{
		// Add defaults here once needed.
	}
	if err := decoder.Decode(&query, r.URL.Query()); err != nil {
		utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest,
			errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String()))
		return
	}
	name := utils.GetName(r)
	newImage, err := runtime.ImageRuntime().NewFromLocal(name)
	if err != nil {
		utils.ImageNotFound(w, name, err)
		return
	}
	dest, err := alltransports.ParseImageName(query.Destination)
	if err != nil {
		utils.Error(w, "invalid destination parameter", http.StatusBadRequest, errors.Errorf("invalid destination parameter %q", query.Destination))
		return
	}
	rtc, err := runtime.GetConfig()
	if err != nil {
		utils.InternalServerError(w, err)
		return
	}
	sc := image.GetSystemContext(rtc.Engine.SignaturePolicyPath, "", false)
	opts := manifests.PushOptions{
		Store:              runtime.GetStore(),
		ImageListSelection: copy2.CopySpecificImages,
		SystemContext:      sc,
	}
	if query.All {
		opts.ImageListSelection = copy2.CopyAllImages
	}
	newD, err := newImage.PushManifest(dest, opts)
	if err != nil {
		utils.InternalServerError(w, err)
		return
	}
	utils.WriteResponse(w, http.StatusOK, newD.String())
}
