package api

import (
	"context"
	"net/http"

	"github.com/go-openapi/strfmt"

	"github.com/grafana/grafana/pkg/api/response"
	"github.com/grafana/grafana/pkg/apimachinery/identity"
	contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
	apimodels "github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions"
	"github.com/grafana/grafana/pkg/services/ngalert/models"
	"github.com/grafana/grafana/pkg/util"
)

// SilenceService is the service for managing and authenticating silences access in Grafana AM.
type SilenceService interface {
	GetSilence(ctx context.Context, user identity.Requester, silenceID string) (*models.Silence, error)
	ListSilences(ctx context.Context, user identity.Requester, filter []string) ([]*models.Silence, error)
	CreateSilence(ctx context.Context, user identity.Requester, ps models.Silence) (string, error)
	UpdateSilence(ctx context.Context, user identity.Requester, ps models.Silence) (string, error)
	DeleteSilence(ctx context.Context, user identity.Requester, silenceID string) error
	WithAccessControlMetadata(ctx context.Context, user identity.Requester, silencesWithMetadata ...*models.SilenceWithMetadata) error
	WithRuleMetadata(ctx context.Context, user identity.Requester, silences ...*models.SilenceWithMetadata) error
}

// RouteGetSilence is the single silence GET endpoint for Grafana AM.
func (srv AlertmanagerSrv) RouteGetSilence(c *contextmodel.ReqContext, silenceID string) response.Response {
	silence, err := srv.silenceSvc.GetSilence(c.Req.Context(), c.SignedInUser, silenceID)
	if err != nil {
		return response.ErrOrFallback(http.StatusInternalServerError, "failed to get silence", err)
	}

	silenceWithMetadata := &models.SilenceWithMetadata{
		Silence: silence,
	}
	if c.QueryBool("accesscontrol") {
		if err := srv.silenceSvc.WithAccessControlMetadata(c.Req.Context(), c.SignedInUser, silenceWithMetadata); err != nil {
			srv.log.Error("failed to get silence access control metadata", "silenceID", silenceID, "error", err)
		}
	}
	if c.QueryBool("ruleMetadata") {
		if err := srv.silenceSvc.WithRuleMetadata(c.Req.Context(), c.SignedInUser, silenceWithMetadata); err != nil {
			srv.log.Error("failed to get silence rule metadata", "silenceID", silenceID, "error", err)
		}
	}

	return response.JSON(http.StatusOK, SilenceToGettableGrafanaSilence(silenceWithMetadata))
}

// RouteGetSilences is the silence list GET endpoint for Grafana AM.
func (srv AlertmanagerSrv) RouteGetSilences(c *contextmodel.ReqContext) response.Response {
	silences, err := srv.silenceSvc.ListSilences(c.Req.Context(), c.SignedInUser, c.QueryStrings("filter"))
	if err != nil {
		return response.ErrOrFallback(http.StatusInternalServerError, "failed to list silence", err)
	}

	silencesWithMetadata := withEmptyMetadata(silences...)
	if c.QueryBool("accesscontrol") {
		if err := srv.silenceSvc.WithAccessControlMetadata(c.Req.Context(), c.SignedInUser, silencesWithMetadata...); err != nil {
			srv.log.Error("failed to get silence access control metadata", "error", err)
		}
	}
	if c.QueryBool("ruleMetadata") {
		if err := srv.silenceSvc.WithRuleMetadata(c.Req.Context(), c.SignedInUser, silencesWithMetadata...); err != nil {
			srv.log.Error("failed to get silence rule metadata", "error", err)
		}
	}
	return response.JSON(http.StatusOK, SilencesToGettableGrafanaSilences(silencesWithMetadata))
}

// RouteCreateSilence is the silence POST (create + update) endpoint for Grafana AM.
func (srv AlertmanagerSrv) RouteCreateSilence(c *contextmodel.ReqContext, postableSilence apimodels.PostableSilence) response.Response {
	err := postableSilence.Validate(strfmt.Default)
	if err != nil {
		srv.log.Error("Silence failed validation", "error", err)
		return ErrResp(http.StatusBadRequest, err, "silence failed validation")
	}
	action := srv.silenceSvc.UpdateSilence
	if postableSilence.ID == "" {
		action = srv.silenceSvc.CreateSilence
	}
	silenceID, err := action(c.Req.Context(), c.SignedInUser, PostableSilenceToSilence(postableSilence))
	if err != nil {
		return response.ErrOrFallback(http.StatusInternalServerError, "failed to create/update silence", err)
	}

	return response.JSON(http.StatusAccepted, apimodels.PostSilencesOKBody{
		SilenceID: silenceID,
	})
}

// RouteDeleteSilence is the silence DELETE endpoint for Grafana AM.
func (srv AlertmanagerSrv) RouteDeleteSilence(c *contextmodel.ReqContext, silenceID string) response.Response {
	if err := srv.silenceSvc.DeleteSilence(c.Req.Context(), c.SignedInUser, silenceID); err != nil {
		return response.ErrOrFallback(http.StatusInternalServerError, "failed to delete silence", err)
	}
	return response.JSON(http.StatusOK, util.DynMap{"message": "silence deleted"})
}

// withEmptyMetadata creates a slice of SilenceWithMetadata from a slice of Silence where the metadata for each silence
// is empty.
func withEmptyMetadata(silences ...*models.Silence) []*models.SilenceWithMetadata {
	silencesWithMetadata := make([]*models.SilenceWithMetadata, 0, len(silences))
	for _, silence := range silences {
		silencesWithMetadata = append(silencesWithMetadata, &models.SilenceWithMetadata{
			Silence: silence,
		})
	}
	return silencesWithMetadata
}
