package tempproxy

import (
	"crypto/tls"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"net/url"
	"strings"
	"time"
)

// HTTPClient communicates with pulse-sensor-proxy via HTTPS
type HTTPClient struct {
	baseURL    string
	authToken  string
	httpClient *http.Client
	timeout    time.Duration
}

// NewHTTPClient creates a new HTTP-based proxy client
func NewHTTPClient(baseURL, authToken string) *HTTPClient {
	// Create HTTP client with TLS and reasonable timeouts
	httpClient := &http.Client{
		Timeout: defaultTimeout,
		Transport: &http.Transport{
			TLSClientConfig: &tls.Config{
				MinVersion:         tls.VersionTLS12,
				InsecureSkipVerify: true, // Accept self-signed certificates from sensor-proxy
			},
			MaxIdleConns:        10,
			IdleConnTimeout:     90 * time.Second,
			DisableCompression:  false,
			DisableKeepAlives:   false,
			MaxIdleConnsPerHost: 2,
		},
	}

	return &HTTPClient{
		baseURL:    strings.TrimSuffix(baseURL, "/"),
		authToken:  authToken,
		httpClient: httpClient,
		timeout:    defaultTimeout,
	}
}

// IsAvailable checks if the HTTP proxy is accessible
// For HTTP mode, we consider it available if URL and token are configured
func (c *HTTPClient) IsAvailable() bool {
	return c.baseURL != "" && c.authToken != ""
}

// GetTemperature fetches temperature data from a node via HTTP
func (c *HTTPClient) GetTemperature(nodeHost string) (string, error) {
	if !c.IsAvailable() {
		return "", &ProxyError{
			Type:      ErrorTypeTransport,
			Message:   "HTTP proxy not configured",
			Retryable: false,
		}
	}

	// Build request URL
	reqURL := fmt.Sprintf("%s/temps?node=%s", c.baseURL, url.QueryEscape(nodeHost))

	// Create request
	req, err := http.NewRequest(http.MethodGet, reqURL, nil)
	if err != nil {
		return "", &ProxyError{
			Type:      ErrorTypeTransport,
			Message:   "failed to create HTTP request",
			Retryable: false,
			Wrapped:   err,
		}
	}

	// Add authorization header
	req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", c.authToken))
	req.Header.Set("Accept", "application/json")

	// Execute request with retries
	var lastErr error
	for attempt := 0; attempt < maxRetries; attempt++ {
		if attempt > 0 {
			backoff := calculateBackoff(attempt)
			time.Sleep(backoff)
		}

		resp, err := c.httpClient.Do(req)
		if err != nil {
			lastErr = &ProxyError{
				Type:      ErrorTypeTransport,
				Message:   fmt.Sprintf("HTTP request failed (attempt %d/%d)", attempt+1, maxRetries),
				Retryable: true,
				Wrapped:   err,
			}
			continue
		}

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

		if err != nil {
			lastErr = &ProxyError{
				Type:      ErrorTypeTransport,
				Message:   "failed to read response body",
				Retryable: true,
				Wrapped:   err,
			}
			continue
		}

		// Check HTTP status
		if resp.StatusCode == http.StatusUnauthorized {
			return "", &ProxyError{
				Type:      ErrorTypeAuth,
				Message:   "authentication failed - invalid token",
				Retryable: false,
			}
		}

		if resp.StatusCode == http.StatusForbidden {
			return "", &ProxyError{
				Type:      ErrorTypeAuth,
				Message:   "node not allowed by proxy",
				Retryable: false,
			}
		}

		if resp.StatusCode == http.StatusTooManyRequests {
			lastErr = &ProxyError{
				Type:      ErrorTypeTransport,
				Message:   "rate limit exceeded",
				Retryable: true,
			}
			continue
		}

		if resp.StatusCode != http.StatusOK {
			return "", &ProxyError{
				Type:      ErrorTypeTransport,
				Message:   fmt.Sprintf("HTTP %d: %s", resp.StatusCode, string(body)),
				Retryable: resp.StatusCode >= 500,
			}
		}

		// Parse JSON response
		var jsonResp struct {
			Node        string `json:"node"`
			Temperature string `json:"temperature"` // This is a JSON-encoded string
		}

		if err := json.Unmarshal(body, &jsonResp); err != nil {
			return "", &ProxyError{
				Type:      ErrorTypeTransport,
				Message:   "failed to parse response JSON",
				Retryable: false,
				Wrapped:   err,
			}
		}

		// The temperature field contains JSON-encoded sensor data as a string
		// Return it as-is since the caller expects raw JSON
		return jsonResp.Temperature, nil
	}

	// All retries exhausted
	if lastErr != nil {
		return "", lastErr
	}

	return "", &ProxyError{
		Type:      ErrorTypeUnknown,
		Message:   "all retry attempts failed",
		Retryable: false,
	}
}

// HealthCheck calls the proxy /health endpoint to verify connectivity.
func (c *HTTPClient) HealthCheck() error {
	if !c.IsAvailable() {
		return &ProxyError{
			Type:      ErrorTypeTransport,
			Message:   "HTTP proxy not configured",
			Retryable: false,
		}
	}

	reqURL := fmt.Sprintf("%s/health", c.baseURL)
	req, err := http.NewRequest(http.MethodGet, reqURL, nil)
	if err != nil {
		return &ProxyError{
			Type:      ErrorTypeTransport,
			Message:   "failed to create HTTP request",
			Retryable: false,
			Wrapped:   err,
		}
	}
	req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", c.authToken))
	req.Header.Set("Accept", "application/json")

	resp, err := c.httpClient.Do(req)
	if err != nil {
		return &ProxyError{
			Type:      ErrorTypeTransport,
			Message:   "HTTP request failed",
			Retryable: true,
			Wrapped:   err,
		}
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		body, _ := io.ReadAll(io.LimitReader(resp.Body, 1024))
		return &ProxyError{
			Type:      ErrorTypeTransport,
			Message:   fmt.Sprintf("HTTP %d: %s", resp.StatusCode, strings.TrimSpace(string(body))),
			Retryable: resp.StatusCode >= 500,
		}
	}

	_, _ = io.Copy(io.Discard, resp.Body)
	return nil
}
