package signoz

import (
	"bytes"
	"context"
	"crypto/tls"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"net/url"
	"path"
	"strings"
	"time"

	"github.com/CalmoAI/mcp-server-signoz/internal/domain"
	"github.com/CalmoAI/mcp-server-signoz/internal/infrastructure/config"
	"golang.org/x/time/rate"
)

// Helper function to safely get string from map
func getString(m map[string]interface{}, key string) string {
	if val, ok := m[key]; ok {
		if str, ok := val.(string); ok {
			return str
		}
	}
	return ""
}

// Client implements the SignozRepository interface
type Client struct {
	httpClient  *http.Client
	baseURL     *url.URL
	apiKey      string
	rateLimiter *rate.Limiter
	config      *config.SignozConfig
}

// NewClient creates a new Signoz client
func NewClient(cfg *config.SignozConfig) (*Client, error) {
	if cfg == nil {
		return nil, fmt.Errorf("config is required")
	}

	baseURL, err := url.Parse(cfg.URL)
	if err != nil {
		return nil, fmt.Errorf("invalid Signoz URL: %w", err)
	}

	// Configure HTTP client with proper timeouts and connection pooling
	transport := &http.Transport{
		MaxIdleConns:        cfg.MaxConnections,
		MaxIdleConnsPerHost: cfg.MaxConnections / 4,
		IdleConnTimeout:     cfg.IdleTimeout,
		DisableKeepAlives:   false,
		TLSClientConfig: &tls.Config{
			InsecureSkipVerify: cfg.TLSSkipVerify,
		},
	}

	httpClient := &http.Client{
		Transport: transport,
		Timeout:   cfg.Timeout,
	}

	// Rate limiter: 100 requests per minute
	rateLimiter := rate.NewLimiter(rate.Every(600*time.Millisecond), 100)

	return &Client{
		httpClient:  httpClient,
		baseURL:     baseURL,
		apiKey:      cfg.APIKey,
		rateLimiter: rateLimiter,
		config:      cfg,
	}, nil
}

// TestConnection implements SignozRepository interface
func (c *Client) TestConnection(ctx context.Context) (*domain.ConnectionStatus, error) {
	start := time.Now()

	// Try to get version info
	req, err := c.newRequest(ctx, "GET", "/api/v1/version", nil)
	if err != nil {
		return &domain.ConnectionStatus{
			Connected:   false,
			URL:         c.baseURL.String(),
			LastChecked: time.Now(),
			Error:       fmt.Sprintf("failed to create request: %v", err),
		}, nil
	}

	resp, err := c.doRequestWithRetry(ctx, req)
	if err != nil {
		return &domain.ConnectionStatus{
			Connected:   false,
			URL:         c.baseURL.String(),
			LastChecked: time.Now(),
			Error:       fmt.Sprintf("connection failed: %v", err),
		}, nil
	}
	defer resp.Body.Close()

	latency := time.Since(start)

	var versionInfo struct {
		Version string `json:"version"`
	}

	if err := json.NewDecoder(resp.Body).Decode(&versionInfo); err != nil {
		// Connection works but can't parse version
		return &domain.ConnectionStatus{
			Connected:   true,
			URL:         c.baseURL.String(),
			LastChecked: time.Now(),
			Latency:     latency,
		}, nil
	}

	return &domain.ConnectionStatus{
		Connected:   true,
		URL:         c.baseURL.String(),
		Version:     versionInfo.Version,
		LastChecked: time.Now(),
		Latency:     latency,
	}, nil
}

// GetDashboards implements SignozRepository interface
func (c *Client) GetDashboards(ctx context.Context) ([]domain.Dashboard, error) {
	req, err := c.newRequest(ctx, "GET", "/api/v1/dashboards", nil)
	if err != nil {
		return nil, fmt.Errorf("failed to create request: %w", err)
	}

	resp, err := c.doRequestWithRetry(ctx, req)
	if err != nil {
		return nil, fmt.Errorf("failed to get dashboards: %w", err)
	}
	defer resp.Body.Close()

	var response struct {
		Data []struct {
			ID          string    `json:"id"`
			Title       string    `json:"title"`
			Description string    `json:"description"`
			Tags        []string  `json:"tags"`
			CreatedAt   time.Time `json:"created_at"`
			UpdatedAt   time.Time `json:"updated_at"`
			CreatedBy   string    `json:"created_by"`
		} `json:"data"`
	}

	if err := json.NewDecoder(resp.Body).Decode(&response); err != nil {
		return nil, fmt.Errorf("failed to decode response: %w", err)
	}

	dashboards := make([]domain.Dashboard, len(response.Data))
	for i, d := range response.Data {
		dashboards[i] = domain.Dashboard{
			ID:          d.ID,
			Name:        d.Title,
			Description: d.Description,
			Tags:        d.Tags,
			CreatedAt:   d.CreatedAt,
			UpdatedAt:   d.UpdatedAt,
			CreatedBy:   d.CreatedBy,
		}
	}

	return dashboards, nil
}

// GetDashboardDetails implements SignozRepository interface
func (c *Client) GetDashboardDetails(ctx context.Context, dashboardID string) (*domain.Dashboard, error) {
	endpoint := fmt.Sprintf("/api/v1/dashboards/%s", url.PathEscape(dashboardID))
	req, err := c.newRequest(ctx, "GET", endpoint, nil)
	if err != nil {
		return nil, fmt.Errorf("failed to create request: %w", err)
	}

	resp, err := c.doRequestWithRetry(ctx, req)
	if err != nil {
		return nil, fmt.Errorf("failed to get dashboard details: %w", err)
	}
	defer resp.Body.Close()

	var response struct {
		Data struct {
			ID          string    `json:"id"`
			Title       string    `json:"title"`
			Description string    `json:"description"`
			Tags        []string  `json:"tags"`
			CreatedAt   time.Time `json:"created_at"`
			UpdatedAt   time.Time `json:"updated_at"`
			CreatedBy   string    `json:"created_by"`
			Panels      []struct {
				ID          string                 `json:"id"`
				Title       string                 `json:"title"`
				Type        string                 `json:"type"`
				Description string                 `json:"description"`
				Query       string                 `json:"query"`
				Options     map[string]interface{} `json:"options"`
				GridPos     struct {
					X int `json:"x"`
					Y int `json:"y"`
					W int `json:"w"`
					H int `json:"h"`
				} `json:"gridPos"`
			} `json:"panels"`
		} `json:"data"`
	}

	if err := json.NewDecoder(resp.Body).Decode(&response); err != nil {
		return nil, fmt.Errorf("failed to decode response: %w", err)
	}

	d := response.Data
	dashboard := &domain.Dashboard{
		ID:          d.ID,
		Name:        d.Title,
		Description: d.Description,
		Tags:        d.Tags,
		CreatedAt:   d.CreatedAt,
		UpdatedAt:   d.UpdatedAt,
		CreatedBy:   d.CreatedBy,
	}

	// Convert panels
	dashboard.Panels = make([]domain.Panel, len(d.Panels))
	for i, p := range d.Panels {
		dashboard.Panels[i] = domain.Panel{
			ID:          p.ID,
			Title:       p.Title,
			Type:        p.Type,
			Description: p.Description,
			Query:       p.Query,
			Options:     p.Options,
			Position: domain.PanelPosition{
				X:      p.GridPos.X,
				Y:      p.GridPos.Y,
				Width:  p.GridPos.W,
				Height: p.GridPos.H,
			},
		}
	}

	return dashboard, nil
}

// GetDashboardData implements SignozRepository interface
func (c *Client) GetDashboardData(ctx context.Context, dashboardName string, timeRange domain.TimeRange) (*domain.DashboardData, error) {
	// First, find the dashboard by name
	dashboards, err := c.GetDashboards(ctx)
	if err != nil {
		return nil, fmt.Errorf("failed to get dashboards: %w", err)
	}

	var dashboardID string
	for _, d := range dashboards {
		if d.Name == dashboardName {
			dashboardID = d.ID
			break
		}
	}

	if dashboardID == "" {
		return nil, fmt.Errorf("dashboard not found: %s", dashboardName)
	}

	// Get dashboard details to get panel information
	dashboard, err := c.GetDashboardDetails(ctx, dashboardID)
	if err != nil {
		return nil, fmt.Errorf("failed to get dashboard details: %w", err)
	}

	// Fetch data for each panel
	panelData := make([]domain.PanelData, len(dashboard.Panels))
	for i, panel := range dashboard.Panels {
		data, err := c.getPanelData(ctx, panel, timeRange)
		if err != nil {
			panelData[i] = domain.PanelData{
				PanelID: panel.ID,
				Status:  "error",
				Error:   err.Error(),
			}
		} else {
			panelData[i] = domain.PanelData{
				PanelID: panel.ID,
				Data:    data,
				Status:  "success",
			}
		}
	}

	return &domain.DashboardData{
		DashboardID: dashboardID,
		Panels:      panelData,
		TimeRange:   timeRange,
		Timestamp:   time.Now(),
	}, nil
}

// GetServices implements SignozRepository interface
func (c *Client) GetServices(ctx context.Context, timeRange *domain.TimeRange) ([]domain.Service, error) {
	// Create JSON payload like the Python code
	payload := map[string]interface{}{
		"tags": []string{}, // Empty tags array as in Python
	}

	// SigNoz API requires start and end times, use default if not provided
	var startNs, endNs int64
	if timeRange != nil {
		startNs = timeRange.Start.UnixNano()
		endNs = timeRange.End.UnixNano()
	} else {
		// Default to last 24 hours (like Python code default_hours=24)
		now := time.Now()
		endNs = now.UnixNano()
		startNs = now.Add(-24 * time.Hour).UnixNano()
	}

	payload["start"] = fmt.Sprintf("%d", startNs)
	payload["end"] = fmt.Sprintf("%d", endNs)

	endpoint := "/api/v1/services"
	req, err := c.newRequest(ctx, "POST", endpoint, payload)
	if err != nil {
		return nil, fmt.Errorf("failed to create request: %w", err)
	}

	resp, err := c.doRequestWithRetry(ctx, req)
	if err != nil {
		return nil, fmt.Errorf("failed to get services: %w", err)
	}
	defer resp.Body.Close()

	// SigNoz returns an array directly, not wrapped in a "data" field
	var responseData []struct {
		ServiceName string            `json:"serviceName"`
		Environment string            `json:"environment"`
		Tags        map[string]string `json:"tags"`
		Operations  []string          `json:"operations"`
		CallRate    float64           `json:"callRate"`
		ErrorRate   float64           `json:"errorRate"`
		P99Latency  float64           `json:"p99Latency"`
		AvgLatency  float64           `json:"avgLatency"`
		LastSeen    time.Time         `json:"lastSeen"`
	}

	if err := json.NewDecoder(resp.Body).Decode(&responseData); err != nil {
		return nil, fmt.Errorf("failed to decode response: %w", err)
	}

	services := make([]domain.Service, len(responseData))
	for i, s := range responseData {
		services[i] = domain.Service{
			ServiceName: s.ServiceName,
			Environment: s.Environment,
			Tags:        s.Tags,
			Operations:  s.Operations,
			CallRate:    s.CallRate,
			ErrorRate:   s.ErrorRate,
			P99Latency:  s.P99Latency,
			AvgLatency:  s.AvgLatency,
			LastSeen:    s.LastSeen,
		}
	}

	return services, nil
}

// GetAPMMetrics implements SignozRepository interface
func (c *Client) GetAPMMetrics(ctx context.Context, serviceName string, timeRange domain.TimeRange) (*domain.APMMetrics, error) {
	// Use /api/v4/query_range with builder query structure like Python code
	builderQueries := map[string]interface{}{
		"A": map[string]interface{}{
			"dataSource":        "metrics",
			"aggregateOperator": "sum_rate",
			"aggregateAttribute": map[string]interface{}{
				"key":      "signoz_latency.count",
				"dataType": "float64",
				"isColumn": true,
				"type":     "",
			},
			"timeAggregation":  "rate",
			"spaceAggregation": "sum",
			"functions":        []interface{}{},
			"filters": map[string]interface{}{
				"items": []interface{}{
					map[string]interface{}{
						"key": map[string]interface{}{
							"key":      "service.name",
							"dataType": "string",
							"isColumn": false,
							"type":     "resource",
						},
						"op":    "IN",
						"value": []string{serviceName},
					},
				},
				"op": "AND",
			},
			"expression":   "A",
			"disabled":     false,
			"stepInterval": 60,
			"having":       []interface{}{},
			"limit":        nil,
			"orderBy":      []interface{}{},
			"groupBy":      []interface{}{},
			"legend":       "Request Rate",
			"reduceTo":     "avg",
			"queryName":    "A",
		},
	}

	requestBody := map[string]interface{}{
		"start":     timeRange.Start.UnixMilli(),
		"end":       timeRange.End.UnixMilli(),
		"step":      60,
		"variables": map[string]interface{}{},
		"compositeQuery": map[string]interface{}{
			"queryType":      "builder",
			"panelType":      "graph",
			"builderQueries": builderQueries,
		},
	}

	req, err := c.newRequest(ctx, "POST", "/api/v4/query_range", requestBody)
	if err != nil {
		return nil, fmt.Errorf("failed to create request: %w", err)
	}

	resp, err := c.doRequestWithRetry(ctx, req)
	if err != nil {
		return nil, fmt.Errorf("failed to get APM metrics: %w", err)
	}
	defer resp.Body.Close()

	var response struct {
		Data struct {
			Metrics map[string]struct {
				Name        string `json:"name"`
				Unit        string `json:"unit"`
				Description string `json:"description"`
				Data        []struct {
					Timestamp int64   `json:"timestamp"`
					Value     float64 `json:"value"`
				} `json:"data"`
			} `json:"metrics"`
		} `json:"data"`
	}

	if err := json.NewDecoder(resp.Body).Decode(&response); err != nil {
		return nil, fmt.Errorf("failed to decode response: %w", err)
	}

	metrics := make(map[string]domain.Metric)
	for key, m := range response.Data.Metrics {
		dataPoints := make([]domain.DataPoint, len(m.Data))
		for i, dp := range m.Data {
			dataPoints[i] = domain.DataPoint{
				Timestamp: time.Unix(dp.Timestamp, 0),
				Value:     dp.Value,
			}
		}

		metrics[key] = domain.Metric{
			Name:        m.Name,
			Unit:        m.Unit,
			Description: m.Description,
			DataPoints:  dataPoints,
		}
	}

	return &domain.APMMetrics{
		ServiceName: serviceName,
		TimeRange:   timeRange,
		Metrics:     metrics,
	}, nil
}

// ExecuteClickHouseQuery implements SignozRepository interface
func (c *Client) ExecuteClickHouseQuery(ctx context.Context, query domain.ClickHouseQuery) (*domain.QueryResult, error) {
	// Use the same payload structure as Python code with /api/v4/query_range
	requestBody := map[string]interface{}{
		"start":        query.TimeRange.Start.UnixMilli(),
		"end":          query.TimeRange.End.UnixMilli(),
		"step":         60, // Default step
		"variables":    map[string]interface{}{},
		"formatForWeb": true,
		"compositeQuery": map[string]interface{}{
			"queryType": "clickhouse_sql",
			"panelType": "table",
			"fillGaps":  false,
			"chQueries": map[string]interface{}{
				"A": map[string]interface{}{
					"name":     "A",
					"legend":   "",
					"disabled": false,
					"query":    query.SQL,
				},
			},
		},
	}

	req, err := c.newRequest(ctx, "POST", "/api/v4/query_range", requestBody)
	if err != nil {
		return nil, fmt.Errorf("failed to create request: %w", err)
	}

	resp, err := c.doRequestWithRetry(ctx, req)
	if err != nil {
		return nil, fmt.Errorf("failed to execute ClickHouse query: %w", err)
	}
	defer resp.Body.Close()

	// Decode the v4 API response
	var rawResponse map[string]interface{}
	if err := json.NewDecoder(resp.Body).Decode(&rawResponse); err != nil {
		return nil, fmt.Errorf("failed to decode response: %w", err)
	}

	// Extract data - try multiple common v4 API response patterns
	var columns []domain.ColumnInfo
	var rows [][]interface{}

	// Pattern 1: Direct data with result array
	if data, ok := rawResponse["data"]; ok {
		if dataMap, ok := data.(map[string]interface{}); ok {
			// Try pattern: data.result[0].table.{columns,rows}
			if result, ok := dataMap["result"]; ok {
				if resultArray, ok := result.([]interface{}); ok && len(resultArray) > 0 {
					if firstResult, ok := resultArray[0].(map[string]interface{}); ok {
						if table, ok := firstResult["table"]; ok {
							if tableMap, ok := table.(map[string]interface{}); ok {
								// Extract columns
								if cols, ok := tableMap["columns"]; ok {
									if colArray, ok := cols.([]interface{}); ok {
										columns = make([]domain.ColumnInfo, len(colArray))
										for i, col := range colArray {
											if colMap, ok := col.(map[string]interface{}); ok {
												columns[i] = domain.ColumnInfo{
													Name: getString(colMap, "name"),
													Type: getString(colMap, "type"),
												}
											}
										}
									}
								}

								// Extract rows
								if rowsData, ok := tableMap["rows"]; ok {
									if rowArray, ok := rowsData.([]interface{}); ok {
										rows = make([][]interface{}, len(rowArray))
										for i, row := range rowArray {
											if rowSlice, ok := row.([]interface{}); ok {
												rows[i] = rowSlice
											} else {
												// Handle single values or different row formats
												rows[i] = []interface{}{row}
											}
										}
									}
								}
							}
						}
					}
				}
			}

			// Pattern 2: Direct columns and rows in data
			if len(columns) == 0 {
				if cols, ok := dataMap["columns"]; ok {
					if colArray, ok := cols.([]interface{}); ok {
						columns = make([]domain.ColumnInfo, len(colArray))
						for i, col := range colArray {
							if colMap, ok := col.(map[string]interface{}); ok {
								columns[i] = domain.ColumnInfo{
									Name: getString(colMap, "name"),
									Type: getString(colMap, "type"),
								}
							}
						}
					}
				}

				if rowsData, ok := dataMap["rows"]; ok {
					if rowArray, ok := rowsData.([]interface{}); ok {
						rows = make([][]interface{}, len(rowArray))
						for i, row := range rowArray {
							if rowSlice, ok := row.([]interface{}); ok {
								rows[i] = rowSlice
							} else {
								// Handle single values or different row formats
								rows[i] = []interface{}{row}
							}
						}
					}
				}
			}
		}
	}

	// Create a simplified data structure
	d := struct {
		Columns       []domain.ColumnInfo
		Rows          [][]interface{}
		Statistics    struct{ ReadRows, ReadBytes, WrittenRows, WrittenBytes int64 }
		ExecutionTime int64
		QueryID       string
	}{
		Columns: columns,
		Rows:    rows,
		Statistics: struct{ ReadRows, ReadBytes, WrittenRows, WrittenBytes int64 }{
			ReadRows: 0, ReadBytes: 0, WrittenRows: 0, WrittenBytes: 0,
		},
		ExecutionTime: 0,
		QueryID:       "",
	}

	return &domain.QueryResult{
		Columns: d.Columns,
		Rows:    d.Rows,
		Meta: domain.QueryMeta{
			QueryID:       d.QueryID,
			ExecutionTime: time.Duration(d.ExecutionTime) * time.Millisecond,
			RowsAffected:  int64(len(d.Rows)),
			Statistics: domain.QueryStats{
				ReadRows:     d.Statistics.ReadRows,
				ReadBytes:    d.Statistics.ReadBytes,
				WrittenRows:  d.Statistics.WrittenRows,
				WrittenBytes: d.Statistics.WrittenBytes,
			},
		},
	}, nil
}

// ExecuteBuilderQuery implements SignozRepository interface
func (c *Client) ExecuteBuilderQuery(ctx context.Context, query domain.BuilderQuery) (*domain.QueryResult, error) {
	// Use /api/v4/query_range with builder query structure
	builderQueries := map[string]interface{}{
		"A": map[string]interface{}{
			"dataSource":        "metrics",
			"aggregateOperator": query.Aggregation,
			"aggregateAttribute": map[string]interface{}{
				"key":      query.MetricName,
				"dataType": "float64",
				"isColumn": true,
				"type":     "",
			},
			"timeAggregation":  query.Aggregation,
			"spaceAggregation": query.Aggregation,
			"functions":        []interface{}{},
			"filters":          query.Filters,
			"expression":       "A",
			"disabled":         false,
			"stepInterval":     int(query.Step.Seconds()),
			"having":           []interface{}{},
			"limit":            nil,
			"orderBy":          []interface{}{},
			"groupBy":          query.GroupBy,
			"legend":           query.MetricName,
			"reduceTo":         "avg",
			"queryName":        "A",
		},
	}

	requestBody := map[string]interface{}{
		"start":     query.TimeRange.Start.UnixMilli(),
		"end":       query.TimeRange.End.UnixMilli(),
		"step":      int(query.Step.Seconds()),
		"variables": map[string]interface{}{},
		"compositeQuery": map[string]interface{}{
			"queryType":      "builder",
			"panelType":      "graph",
			"builderQueries": builderQueries,
		},
	}

	req, err := c.newRequest(ctx, "POST", "/api/v4/query_range", requestBody)
	if err != nil {
		return nil, fmt.Errorf("failed to create request: %w", err)
	}

	resp, err := c.doRequestWithRetry(ctx, req)
	if err != nil {
		return nil, fmt.Errorf("failed to execute builder query: %w", err)
	}
	defer resp.Body.Close()

	var response struct {
		Data struct {
			Columns []struct {
				Name string `json:"name"`
				Type string `json:"type"`
			} `json:"columns"`
			Rows          [][]interface{} `json:"rows"`
			ExecutionTime int64           `json:"execution_time_ms"`
			QueryID       string          `json:"query_id"`
		} `json:"data"`
	}

	if err := json.NewDecoder(resp.Body).Decode(&response); err != nil {
		return nil, fmt.Errorf("failed to decode response: %w", err)
	}

	d := response.Data
	columns := make([]domain.ColumnInfo, len(d.Columns))
	for i, col := range d.Columns {
		columns[i] = domain.ColumnInfo{
			Name: col.Name,
			Type: col.Type,
		}
	}

	return &domain.QueryResult{
		Columns: columns,
		Rows:    d.Rows,
		Meta: domain.QueryMeta{
			QueryID:       d.QueryID,
			ExecutionTime: time.Duration(d.ExecutionTime) * time.Millisecond,
			RowsAffected:  int64(len(d.Rows)),
		},
	}, nil
}

// GetTracesOrLogs implements SignozRepository interface
func (c *Client) GetTracesOrLogs(ctx context.Context, dataType string, timeRange domain.TimeRange, serviceName string, limit int) ([]domain.TraceLog, error) {
	// Use ClickHouse SQL query with parameterization to prevent SQL injection
	var tableName, sql string
	var params map[string]interface{}

	// Sanitize inputs
	if err := c.validateInputs(dataType, serviceName, limit); err != nil {
		return nil, fmt.Errorf("invalid input parameters: %w", err)
	}

	startTime := timeRange.Start.Format("2006-01-02 15:04:05")
	endTime := timeRange.End.Format("2006-01-02 15:04:05")

	if dataType == "traces" {
		tableName = "signoz_traces.signoz_index_v2"
		// Use parameterized query to prevent SQL injection
		sql = fmt.Sprintf("SELECT traceID, spanID, serviceName, name, timestamp FROM %s WHERE serviceName = {serviceName:String} AND timestamp >= toDateTime64({startTime:String}, 3) AND timestamp <= toDateTime64({endTime:String}, 3) ORDER BY timestamp DESC LIMIT {limit:UInt32}",
			tableName)
		params = map[string]interface{}{
			"serviceName": serviceName,
			"startTime":   startTime,
			"endTime":     endTime,
			"limit":       limit,
		}
	} else {
		tableName = "signoz_logs.logs"
		// Use parameterized query to prevent SQL injection
		sql = fmt.Sprintf("SELECT id, timestamp, severity_text, body FROM %s WHERE service_name = {serviceName:String} AND timestamp >= toDateTime64({startTime:String}, 3) AND timestamp <= toDateTime64({endTime:String}, 3) ORDER BY timestamp DESC LIMIT {limit:UInt32}",
			tableName)
		params = map[string]interface{}{
			"serviceName": serviceName,
			"startTime":   startTime,
			"endTime":     endTime,
			"limit":       limit,
		}
	}

	// Use the same payload structure as ClickHouse query with parameters
	requestBody := map[string]interface{}{
		"start":        timeRange.Start.UnixMilli(),
		"end":          timeRange.End.UnixMilli(),
		"step":         60,
		"variables":    params, // Include parameters for safe query execution
		"formatForWeb": true,
		"compositeQuery": map[string]interface{}{
			"queryType": "clickhouse_sql",
			"panelType": "table",
			"fillGaps":  false,
			"chQueries": map[string]interface{}{
				"A": map[string]interface{}{
					"name":     "A",
					"legend":   "",
					"disabled": false,
					"query":    sql,
				},
			},
		},
	}

	req, err := c.newRequest(ctx, "POST", "/api/v4/query_range", requestBody)
	if err != nil {
		return nil, fmt.Errorf("failed to create request: %w", err)
	}

	resp, err := c.doRequestWithRetry(ctx, req)
	if err != nil {
		return nil, fmt.Errorf("failed to get %s: %w", dataType, err)
	}
	defer resp.Body.Close()

	// v4 API returns a different response structure similar to ClickHouse
	var response struct {
		Data map[string]interface{} `json:"data"`
	}

	if err := json.NewDecoder(resp.Body).Decode(&response); err != nil {
		return nil, fmt.Errorf("failed to decode response: %w", err)
	}

	// Extract rows from the v4 API response
	var result []domain.TraceLog

	if response.Data != nil {
		if rowData, ok := response.Data["rows"]; ok {
			if rowArray, ok := rowData.([]interface{}); ok {
				result = make([]domain.TraceLog, len(rowArray))
				for i, row := range rowArray {
					if rowSlice, ok := row.([]interface{}); ok && len(rowSlice) > 0 {
						// Parse based on the SQL query structure
						item := domain.TraceLog{
							Type:       dataType,
							Timestamp:  time.Now(), // Default if parsing fails
							Service:    serviceName,
							Attributes: make(map[string]interface{}),
						}

						// Extract data based on dataType and query structure
						if dataType == "traces" && len(rowSlice) >= 5 {
							// traceID, spanID, serviceName, name, timestamp
							if traceID, ok := rowSlice[0].(string); ok {
								item.TraceID = traceID
							}
							if spanID, ok := rowSlice[1].(string); ok {
								item.SpanID = spanID
							}
							if name, ok := rowSlice[3].(string); ok {
								item.Operation = name
							}
						} else if dataType == "logs" && len(rowSlice) >= 4 {
							// id, timestamp, severity_text, body
							if id, ok := rowSlice[0].(string); ok {
								item.ID = id
							}
							if body, ok := rowSlice[3].(string); ok {
								item.Message = body
							}
						}

						result[i] = item
					}
				}
			}
		}
	}

	// If no rows found, return empty slice
	if result == nil {
		result = []domain.TraceLog{}
	}

	return result, nil
}

// newRequest creates a new HTTP request with proper headers and authentication
func (c *Client) newRequest(ctx context.Context, method, endpoint string, body interface{}) (*http.Request, error) {
	u := *c.baseURL
	u.Path = path.Join(u.Path, endpoint)

	var buf io.Reader
	if body != nil {
		jsonBody, err := json.Marshal(body)
		if err != nil {
			return nil, fmt.Errorf("failed to marshal request body: %w", err)
		}
		buf = bytes.NewBuffer(jsonBody)
	}

	req, err := http.NewRequestWithContext(ctx, method, u.String(), buf)
	if err != nil {
		return nil, fmt.Errorf("failed to create HTTP request: %w", err)
	}

	// Set headers
	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("Accept", "application/json")
	req.Header.Set("User-Agent", "signoz-mcp-server/1.0.0")

	// Set authentication header if API key is provided
	if c.apiKey != "" {
		req.Header.Set("SIGNOZ-API-KEY", c.apiKey)
	}

	return req, nil
}

// doRequestWithRetry executes an HTTP request with retry logic
func (c *Client) doRequestWithRetry(ctx context.Context, req *http.Request) (*http.Response, error) {
	// Rate limiting
	if err := c.rateLimiter.Wait(ctx); err != nil {
		return nil, fmt.Errorf("rate limiter error: %w", err)
	}

	var lastErr error
	retryDelay := c.config.RetryDelay

	for attempt := 0; attempt <= c.config.RetryAttempts; attempt++ {
		if attempt > 0 {
			select {
			case <-ctx.Done():
				return nil, ctx.Err()
			case <-time.After(retryDelay):
				// Exponential backoff with jitter
				retryDelay = min(retryDelay*2, c.config.MaxRetryDelay)
			}
		}

		resp, err := c.httpClient.Do(req)
		if err != nil {
			lastErr = err
			if !isRetryableError(err) {
				break
			}
			continue
		}

		// Check for HTTP errors
		if resp.StatusCode >= 200 && resp.StatusCode < 300 {
			return resp, nil
		}

		// Read error response
		body, _ := io.ReadAll(resp.Body)
		resp.Body.Close()

		lastErr = fmt.Errorf("HTTP %d: %s", resp.StatusCode, string(body))

		// Don't retry client errors (4xx), except 429 (Too Many Requests)
		if resp.StatusCode >= 400 && resp.StatusCode < 500 && resp.StatusCode != 429 {
			break
		}
	}

	return nil, fmt.Errorf("request failed after %d attempts: %w", c.config.RetryAttempts+1, lastErr)
}

// getPanelData fetches data for a specific panel
func (c *Client) getPanelData(ctx context.Context, panel domain.Panel, timeRange domain.TimeRange) (map[string]interface{}, error) {
	// This is a simplified implementation
	// In a real implementation, you would parse the panel query and execute it appropriately
	requestBody := map[string]interface{}{
		"query": panel.Query,
		"start": timeRange.Start.UnixMilli(), // Use milliseconds like Python
		"end":   timeRange.End.UnixMilli(),   // Use milliseconds like Python
	}

	req, err := c.newRequest(ctx, "POST", "/api/v1/query/panel", requestBody)
	if err != nil {
		return nil, fmt.Errorf("failed to create request: %w", err)
	}

	resp, err := c.doRequestWithRetry(ctx, req)
	if err != nil {
		return nil, fmt.Errorf("failed to get panel data: %w", err)
	}
	defer resp.Body.Close()

	var result map[string]interface{}
	if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
		return nil, fmt.Errorf("failed to decode panel data: %w", err)
	}

	return result, nil
}

// isRetryableError determines if an error is retryable
func isRetryableError(err error) bool {
	if err == nil {
		return false
	}

	// Check for network errors, timeouts, etc.
	errStr := strings.ToLower(err.Error())
	retryableErrors := []string{
		"connection refused",
		"connection reset",
		"timeout",
		"temporary failure",
		"network unreachable",
		"no such host",
	}

	for _, retryableErr := range retryableErrors {
		if strings.Contains(errStr, retryableErr) {
			return true
		}
	}

	return false
}

// min returns the minimum of two time.Duration values
func min(a, b time.Duration) time.Duration {
	if a < b {
		return a
	}
	return b
}

// validateInputs validates input parameters to prevent SQL injection and other attacks
func (c *Client) validateInputs(dataType, serviceName string, limit int) error {
	// Validate dataType
	if dataType != "traces" && dataType != "logs" {
		return fmt.Errorf("invalid dataType: must be 'traces' or 'logs', got '%s'", dataType)
	}

	// Validate serviceName - prevent SQL injection attempts
	if serviceName == "" {
		return fmt.Errorf("serviceName cannot be empty")
	}

	// Check for dangerous SQL injection characters (parameterized queries handle SQL keywords safely)
	dangerousChars := []string{
		"'", "\"", ";", "--", "/*", "*/",
	}

	for _, char := range dangerousChars {
		if strings.Contains(serviceName, char) {
			return fmt.Errorf("serviceName contains potentially dangerous characters: '%s'", char)
		}
	}

	// Validate limit
	if limit <= 0 || limit > 10000 {
		return fmt.Errorf("limit must be between 1 and 10000, got %d", limit)
	}

	return nil
}

// Ensure Client implements SignozRepository interface
var _ domain.SignozRepository = (*Client)(nil)
