package metrics

import (
	"context"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"net/url"
	"time"

	"github.com/pkg/errors"
)

// PrometheusAPIClient implements PrometheusClient using Prometheus HTTP API
type PrometheusAPIClient struct {
	endpoint string
	client   *http.Client
}

// NewPrometheusAPIClient creates a new Prometheus API client
func NewPrometheusAPIClient(endpoint string) *PrometheusAPIClient {
	return &PrometheusAPIClient{
		endpoint: endpoint,
		client: &http.Client{
			Timeout: 30 * time.Second,
		},
	}
}

// QueryResponse represents a Prometheus query response
type QueryResponse struct {
	Status string `json:"status"`
	Data   struct {
		ResultType string `json:"resultType"`
		Result     []struct {
			Metric map[string]string `json:"metric"`
			Value  []interface{}      `json:"value"`
		} `json:"result"`
	} `json:"data"`
}

// Query executes a Prometheus query
func (c *PrometheusAPIClient) Query(ctx context.Context, query string) ([]MetricValue, error) {
	// Build query URL
	queryURL := fmt.Sprintf("%s/api/v1/query", c.endpoint)
	params := url.Values{}
	params.Set("query", query)

	req, err := http.NewRequestWithContext(ctx, "GET", queryURL+"?"+params.Encode(), nil)
	if err != nil {
		return nil, errors.Wrap(err, "failed to create request")
	}

	resp, err := c.client.Do(req)
	if err != nil {
		return nil, errors.Wrap(err, "failed to execute request")
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		body, _ := io.ReadAll(resp.Body)
		return nil, fmt.Errorf("prometheus query failed: %d %s - %s", resp.StatusCode, resp.Status, string(body))
	}

	var queryResp QueryResponse
	if err := json.NewDecoder(resp.Body).Decode(&queryResp); err != nil {
		return nil, errors.Wrap(err, "failed to decode response")
	}

	if queryResp.Status != "success" {
		return nil, fmt.Errorf("prometheus query returned error status: %s", queryResp.Status)
	}

	// Convert to MetricValue slice
	var values []MetricValue
	for _, result := range queryResp.Data.Result {
		if len(result.Value) < 2 {
			continue
		}

		// Parse timestamp (Unix timestamp in seconds)
		timestampFloat, ok := result.Value[0].(float64)
		if !ok {
			continue
		}
		timestamp := time.Unix(int64(timestampFloat), 0)

		// Parse value
		var value float64
		switch v := result.Value[1].(type) {
		case string:
			if _, err := fmt.Sscanf(v, "%f", &value); err != nil {
				continue
			}
		case float64:
			value = v
		default:
			continue
		}

		values = append(values, MetricValue{
			Value:     value,
			Timestamp: timestamp,
			Labels:    result.Metric,
		})
	}

	return values, nil
}

