package queryhistory

import (
	"net/http"

	"github.com/grafana/grafana/pkg/api/response"
	"github.com/grafana/grafana/pkg/api/routing"
	"github.com/grafana/grafana/pkg/middleware"
	"github.com/grafana/grafana/pkg/models"
	"github.com/grafana/grafana/pkg/tsdb/legacydata"
	"github.com/grafana/grafana/pkg/util"
	"github.com/grafana/grafana/pkg/web"
)

func (s *QueryHistoryService) registerAPIEndpoints() {
	s.RouteRegister.Group("/api/query-history", func(entities routing.RouteRegister) {
		entities.Post("/", middleware.ReqSignedIn, routing.Wrap(s.createHandler))
		entities.Get("/", middleware.ReqSignedIn, routing.Wrap(s.searchHandler))
		entities.Delete("/:uid", middleware.ReqSignedIn, routing.Wrap(s.deleteHandler))
		entities.Post("/star/:uid", middleware.ReqSignedIn, routing.Wrap(s.starHandler))
		entities.Delete("/star/:uid", middleware.ReqSignedIn, routing.Wrap(s.unstarHandler))
		entities.Patch("/:uid", middleware.ReqSignedIn, routing.Wrap(s.patchCommentHandler))
		// Remove migrate endpoint in Grafana v10 as breaking change
		entities.Post("/migrate", middleware.ReqSignedIn, routing.Wrap(s.migrateHandler))
	})
}

// swagger:route POST /query-history query_history createQuery
//
// Add query to query history.
//
// Adds new query to query history.
//
// Responses:
// 200: getQueryHistoryResponse
// 400: badRequestError
// 401: unauthorisedError
// 500: internalServerError
func (s *QueryHistoryService) createHandler(c *models.ReqContext) response.Response {
	cmd := CreateQueryInQueryHistoryCommand{}
	if err := web.Bind(c.Req, &cmd); err != nil {
		return response.Error(http.StatusBadRequest, "bad request data", err)
	}

	query, err := s.CreateQueryInQueryHistory(c.Req.Context(), c.SignedInUser, cmd)
	if err != nil {
		return response.Error(http.StatusInternalServerError, "Failed to create query history", err)
	}

	return response.JSON(http.StatusOK, QueryHistoryResponse{Result: query})
}

// swagger:route GET /query-history query_history searchQueries
//
// Query history search.
//
// Returns a list of queries in the query history that matches the search criteria.
// Query history search supports pagination. Use the `limit` parameter to control the maximum number of queries returned; the default limit is 100.
// You can also use the `page` query parameter to fetch queries from any page other than the first one.
//
// Responses:
// 200: getQueryHistorySearchResponse
// 401: unauthorisedError
// 500: internalServerError
func (s *QueryHistoryService) searchHandler(c *models.ReqContext) response.Response {
	timeRange := legacydata.NewDataTimeRange(c.Query("from"), c.Query("to"))

	query := SearchInQueryHistoryQuery{
		DatasourceUIDs: c.QueryStrings("datasourceUid"),
		SearchString:   c.Query("searchString"),
		OnlyStarred:    c.QueryBoolWithDefault("onlyStarred", false),
		Sort:           c.Query("sort"),
		Page:           c.QueryInt("page"),
		Limit:          c.QueryInt("limit"),
		From:           timeRange.GetFromAsSecondsEpoch(),
		To:             timeRange.GetToAsSecondsEpoch(),
	}

	result, err := s.SearchInQueryHistory(c.Req.Context(), c.SignedInUser, query)
	if err != nil {
		return response.Error(http.StatusInternalServerError, "Failed to get query history", err)
	}

	return response.JSON(http.StatusOK, QueryHistorySearchResponse{Result: result})
}

// swagger:route DELETE /query-history/{query_history_uid} query_history deleteQuery
//
// Delete query in query history.
//
// Deletes an existing query in query history as specified by the UID. This operation cannot be reverted.
//
// Responses:
// 200: getQueryHistoryDeleteQueryResponse
// 401: unauthorisedError
// 500: internalServerError
func (s *QueryHistoryService) deleteHandler(c *models.ReqContext) response.Response {
	queryUID := web.Params(c.Req)[":uid"]
	if len(queryUID) > 0 && !util.IsValidShortUID(queryUID) {
		return response.Error(http.StatusNotFound, "Query in query history not found", nil)
	}

	id, err := s.DeleteQueryFromQueryHistory(c.Req.Context(), c.SignedInUser, queryUID)
	if err != nil {
		return response.Error(http.StatusInternalServerError, "Failed to delete query from query history", err)
	}

	return response.JSON(http.StatusOK, QueryHistoryDeleteQueryResponse{
		Message: "Query deleted",
		ID:      id,
	})
}

// swagger:route PATCH /query-history/{query_history_uid} query_history patchQueryComment
//
// Update comment for query in query history.
//
// Updates comment for query in query history as specified by the UID.
//
// Responses:
// 200: getQueryHistoryResponse
// 400: badRequestError
// 401: unauthorisedError
// 500: internalServerError
func (s *QueryHistoryService) patchCommentHandler(c *models.ReqContext) response.Response {
	queryUID := web.Params(c.Req)[":uid"]
	if len(queryUID) > 0 && !util.IsValidShortUID(queryUID) {
		return response.Error(http.StatusNotFound, "Query in query history not found", nil)
	}

	cmd := PatchQueryCommentInQueryHistoryCommand{}
	if err := web.Bind(c.Req, &cmd); err != nil {
		return response.Error(http.StatusBadRequest, "bad request data", err)
	}

	query, err := s.PatchQueryCommentInQueryHistory(c.Req.Context(), c.SignedInUser, queryUID, cmd)
	if err != nil {
		return response.Error(http.StatusInternalServerError, "Failed to update comment of query in query history", err)
	}

	return response.JSON(http.StatusOK, QueryHistoryResponse{Result: query})
}

// swagger:route POST /query-history/star/{query_history_uid} query_history starQuery
//
// Add star to query in query history.
//
// Adds star to query in query history as specified by the UID.
//
// Responses:
// 200: getQueryHistoryResponse
// 401: unauthorisedError
// 500: internalServerError
func (s *QueryHistoryService) starHandler(c *models.ReqContext) response.Response {
	queryUID := web.Params(c.Req)[":uid"]
	if len(queryUID) > 0 && !util.IsValidShortUID(queryUID) {
		return response.Error(http.StatusNotFound, "Query in query history not found", nil)
	}

	query, err := s.StarQueryInQueryHistory(c.Req.Context(), c.SignedInUser, queryUID)
	if err != nil {
		return response.Error(http.StatusInternalServerError, "Failed to star query in query history", err)
	}

	return response.JSON(http.StatusOK, QueryHistoryResponse{Result: query})
}

// swagger:route DELETE /query-history/star/{query_history_uid} query_history unstarQuery
//
// Remove star to query in query history.
//
// Removes star from query in query history as specified by the UID.
//
// Responses:
// 200: getQueryHistoryResponse
// 401: unauthorisedError
// 500: internalServerError
func (s *QueryHistoryService) unstarHandler(c *models.ReqContext) response.Response {
	queryUID := web.Params(c.Req)[":uid"]
	if len(queryUID) > 0 && !util.IsValidShortUID(queryUID) {
		return response.Error(http.StatusNotFound, "Query in query history not found", nil)
	}

	query, err := s.UnstarQueryInQueryHistory(c.Req.Context(), c.SignedInUser, queryUID)
	if err != nil {
		return response.Error(http.StatusInternalServerError, "Failed to unstar query in query history", err)
	}

	return response.JSON(http.StatusOK, QueryHistoryResponse{Result: query})
}

// swagger:route POST /query-history/migrate query_history migrateQueries
//
// Migrate queries to query history.
//
// Adds multiple queries to query history.
//
// Responses:
// 200: getQueryHistoryMigrationResponse
// 400: badRequestError
// 401: unauthorisedError
// 500: internalServerError
func (s *QueryHistoryService) migrateHandler(c *models.ReqContext) response.Response {
	cmd := MigrateQueriesToQueryHistoryCommand{}
	if err := web.Bind(c.Req, &cmd); err != nil {
		return response.Error(http.StatusBadRequest, "bad request data", err)
	}

	totalCount, starredCount, err := s.MigrateQueriesToQueryHistory(c.Req.Context(), c.SignedInUser, cmd)
	if err != nil {
		return response.Error(http.StatusInternalServerError, "Failed to migrate query history", err)
	}

	return response.JSON(http.StatusOK, QueryHistoryMigrationResponse{Message: "Query history successfully migrated", TotalCount: totalCount, StarredCount: starredCount})
}

// swagger:parameters starQuery patchQueryComment deleteQuery unstarQuery
type QueryHistoryByUID struct {
	// in:path
	// required:true
	UID string `json:"query_history_uid"`
}

// swagger:parameters searchQueries
type SearchQueriesParams struct {
	// List of data source UIDs to search for
	// in:query
	// required: false
	// type: array
	// collectionFormat: multi
	DatasourceUid []string `json:"datasourceUid"`
	// Text inside query or comments that is searched for
	// in:query
	// required: false
	SearchString string `json:"searchString"`
	// Flag indicating if only starred queries should be returned
	// in:query
	// required: false
	OnlyStarred bool `json:"onlyStarred"`
	// Sort method
	// in:query
	// required: false
	// default: time-desc
	// Enum: time-desc,time-asc
	Sort string `json:"sort"`
	// Use this parameter to access hits beyond limit. Numbering starts at 1. limit param acts as page size.
	// in:query
	// required: false
	Page int `json:"page"`
	// Limit the number of returned results
	// in:query
	// required: false
	Limit int `json:"limit"`
	// From range for the query history search
	// in:query
	// required: false
	From int64 `json:"from"`
	// To range for the query history search
	// in:query
	// required: false
	To int64 `json:"to"`
}

// swagger:parameters createQuery
type CreateQueryParams struct {
	// in:body
	// required:true
	Body CreateQueryInQueryHistoryCommand `json:"body"`
}

// swagger:parameters patchQueryComment
type PatchQueryCommentParams struct {
	// in:body
	// required:true
	Body PatchQueryCommentInQueryHistoryCommand `json:"body"`
}

// swagger:parameters migrateQueries
type MigrateQueriesParams struct {
	// in:body
	// required:true
	Body MigrateQueriesToQueryHistoryCommand `json:"body"`
}

//swagger:response getQueryHistorySearchResponse
type GetQueryHistorySearchResponse struct {
	// in: body
	Body QueryHistorySearchResponse `json:"body"`
}

// swagger:response getQueryHistoryResponse
type GetQueryHistoryResponse struct {
	// in: body
	Body QueryHistoryResponse `json:"body"`
}

// swagger:response getQueryHistoryDeleteQueryResponse
type GetQueryHistoryDeleteQueryResponse struct {
	// in: body
	Body QueryHistoryDeleteQueryResponse `json:"body"`
}

// swagger:response getQueryHistoryMigrationResponse
type GetQueryHistoryMigrationResponse struct {
	// in: body
	Body QueryHistoryMigrationResponse `json:"body"`
}
