package promlib

import (
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"time"

	"github.com/grafana/grafana-plugin-sdk-go/backend"
	sdkapi "github.com/grafana/grafana-plugin-sdk-go/experimental/apis/data/v0alpha1"

	"github.com/grafana/grafana/pkg/promlib/models"
)

const (
	refID = "__healthcheck__"
)

func (s *Service) CheckHealth(ctx context.Context, req *backend.CheckHealthRequest) (*backend.CheckHealthResult,
	error) {
	ds, err := s.getInstance(ctx, req.PluginContext)

	// check that the datasource exists
	if err != nil {
		return getHealthCheckMessage("error getting datasource info", err)
	}

	if ds == nil {
		return getHealthCheckMessage("", errors.New("invalid datasource info received"))
	}

	logger := s.logger.FromContext(ctx)

	hc, err := healthcheck(ctx, req, ds)
	if err != nil {
		logger.Warn("Error performing prometheus healthcheck", "err", err.Error())
		return nil, err
	}

	heuristics, err := getHeuristics(ctx, ds, logger)
	if err != nil {
		logger.Warn("Failed to get prometheus heuristics", "err", err.Error())
	} else {
		jsonDetails, err := json.Marshal(heuristics)
		if err != nil {
			logger.Warn("Failed to marshal heuristics", "err", err)
		} else {
			hc.JSONDetails = jsonDetails
		}
	}

	return hc, nil
}

func healthcheck(ctx context.Context, req *backend.CheckHealthRequest, i *instance) (*backend.CheckHealthResult, error) {
	qm := models.QueryModel{
		UtcOffsetSec: 0,
		CommonQueryProperties: sdkapi.CommonQueryProperties{
			RefID: refID,
		},
		PrometheusQueryProperties: models.PrometheusQueryProperties{
			Expr:    "1+1",
			Instant: true,
		},
	}
	b, _ := json.Marshal(&qm)

	query := backend.DataQuery{
		RefID: refID,
		TimeRange: backend.TimeRange{
			From: time.Unix(1, 0).UTC(),
			To:   time.Unix(4, 0).UTC(),
		},
		JSON: b,
	}
	resp, err := i.queryData.Execute(ctx, &backend.QueryDataRequest{
		PluginContext: req.PluginContext,
		Queries:       []backend.DataQuery{query},
	})

	if err != nil {
		return getHealthCheckMessage("There was an error returned querying the Prometheus API.", err)
	}

	if resp.Responses[refID].Error != nil {
		return getHealthCheckMessage("There was an error returned querying the Prometheus API.",
			errors.New(resp.Responses[refID].Error.Error()))
	}

	return getHealthCheckMessage("Successfully queried the Prometheus API.", nil)
}

func getHealthCheckMessage(message string, err error) (*backend.CheckHealthResult, error) {
	if err == nil {
		return &backend.CheckHealthResult{
			Status:  backend.HealthStatusOk,
			Message: message,
		}, nil
	}

	errorMessage := fmt.Sprintf("%s - %s", err.Error(), message)

	return &backend.CheckHealthResult{
		Status:  backend.HealthStatusError,
		Message: errorMessage,
	}, nil
}
