package containers

import (
	"net/http"

	portainer "github.com/portainer/portainer/api"
	"github.com/portainer/portainer/api/docker/consts"
	"github.com/portainer/portainer/api/docker/images"
	"github.com/portainer/portainer/api/http/middlewares"
	"github.com/portainer/portainer/api/internal/authorization"
	httperror "github.com/portainer/portainer/pkg/libhttp/error"
	"github.com/portainer/portainer/pkg/libhttp/request"
	"github.com/portainer/portainer/pkg/libhttp/response"

	"github.com/rs/zerolog/log"
)

type RecreatePayload struct {
	// PullImage if true will pull the image
	PullImage bool `json:"PullImage"`
}

func (r RecreatePayload) Validate(request *http.Request) error {
	return nil
}

func (handler *Handler) recreate(w http.ResponseWriter, r *http.Request) *httperror.HandlerError {
	containerID, err := request.RetrieveRouteVariableValue(r, "containerId")
	if err != nil {
		return httperror.BadRequest("Invalid containerId", err)
	}

	var payload RecreatePayload
	if err := request.DecodeAndValidateJSONPayload(r, &payload); err != nil {
		return httperror.BadRequest("Invalid request payload", err)
	}

	endpoint, err := middlewares.FetchEndpoint(r)
	if err != nil {
		return httperror.NotFound("Unable to find an environment on request context", err)
	}

	if err := handler.bouncer.AuthorizedEndpointOperation(r, endpoint); err != nil {
		return httperror.Forbidden("Permission denied to force update service", err)
	}

	agentTargetHeader := r.Header.Get(portainer.PortainerAgentTargetHeader)

	newContainer, err := handler.containerService.Recreate(r.Context(), endpoint, containerID, payload.PullImage, "", agentTargetHeader)
	if err != nil {
		return httperror.InternalServerError("Error recreating container", err)
	}

	handler.updateWebhook(containerID, newContainer.ID)
	handler.createResourceControl(containerID, newContainer.ID)

	go func() {
		images.EvictImageStatus(containerID)
		images.EvictImageStatus(newContainer.Config.Labels[consts.ComposeStackNameLabel])
		images.EvictImageStatus(newContainer.Config.Labels[consts.SwarmServiceIDLabel])
	}()

	return response.JSON(w, newContainer)
}

func (handler *Handler) createResourceControl(oldContainerId string, newContainerId string) {
	resourceControls, err := handler.dataStore.ResourceControl().ReadAll()
	if err != nil {
		log.Error().Err(err).Msg("Exporting Resource Controls")

		return
	}

	resourceControl := authorization.GetResourceControlByResourceIDAndType(oldContainerId, portainer.ContainerResourceControl, resourceControls)
	if resourceControl == nil {
		return
	}

	resourceControl.ResourceID = newContainerId
	if err := handler.dataStore.ResourceControl().Create(resourceControl); err != nil {
		log.Error().Err(err).Str("containerId", newContainerId).Msg("Failed to create new resource control for container")
	}
}

func (handler *Handler) updateWebhook(oldContainerId string, newContainerId string) {
	webhook, err := handler.dataStore.Webhook().WebhookByResourceID(oldContainerId)
	if err != nil {
		log.Error().Err(err).Str("containerId", oldContainerId).Msg("cannot find webhook by containerId")

		return
	}

	webhook.ResourceID = newContainerId
	if err := handler.dataStore.Webhook().Update(webhook.ID, webhook); err != nil {
		log.Error().Err(err).Int("webhookId", int(webhook.ID)).Msg("cannot update webhook")
	}
}
