package integration

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

	"chorus.services/bzzz/pkg/config"
)

// SlurpClient handles HTTP communication with SLURP endpoints
type SlurpClient struct {
	baseURL    string
	apiKey     string
	timeout    time.Duration
	retryCount int
	retryDelay time.Duration
	httpClient *http.Client
}

// SlurpEvent represents a SLURP event structure
type SlurpEvent struct {
	EventType   string                 `json:"event_type"`
	Path        string                 `json:"path"`
	Content     string                 `json:"content"`
	Severity    int                    `json:"severity"`
	CreatedBy   string                 `json:"created_by"`
	Metadata    map[string]interface{} `json:"metadata"`
	Tags        []string               `json:"tags,omitempty"`
	Timestamp   time.Time              `json:"timestamp"`
}

// EventResponse represents the response from SLURP API
type EventResponse struct {
	Success   bool   `json:"success"`
	EventID   string `json:"event_id,omitempty"`
	Message   string `json:"message,omitempty"`
	Error     string `json:"error,omitempty"`
	Timestamp time.Time `json:"timestamp"`
}

// BatchEventRequest represents a batch of events to be sent to SLURP
type BatchEventRequest struct {
	Events []SlurpEvent `json:"events"`
	Source string       `json:"source"`
}

// BatchEventResponse represents the response for batch event creation
type BatchEventResponse struct {
	Success      bool              `json:"success"`
	ProcessedCount int             `json:"processed_count"`
	FailedCount    int             `json:"failed_count"`
	EventIDs     []string          `json:"event_ids,omitempty"`
	Errors       []string          `json:"errors,omitempty"`
	Message      string            `json:"message,omitempty"`
	Timestamp    time.Time         `json:"timestamp"`
}

// HealthResponse represents SLURP service health status
type HealthResponse struct {
	Status      string    `json:"status"`
	Version     string    `json:"version,omitempty"`
	Uptime      string    `json:"uptime,omitempty"`
	Timestamp   time.Time `json:"timestamp"`
}

// NewSlurpClient creates a new SLURP API client
func NewSlurpClient(config config.SlurpConfig) *SlurpClient {
	return &SlurpClient{
		baseURL:    strings.TrimSuffix(config.BaseURL, "/"),
		apiKey:     config.APIKey,
		timeout:    config.Timeout,
		retryCount: config.RetryCount,
		retryDelay: config.RetryDelay,
		httpClient: &http.Client{
			Timeout: config.Timeout,
		},
	}
}

// CreateEvent sends a single event to SLURP
func (c *SlurpClient) CreateEvent(ctx context.Context, event SlurpEvent) (*EventResponse, error) {
	url := fmt.Sprintf("%s/api/events", c.baseURL)
	
	eventData, err := json.Marshal(event)
	if err != nil {
		return nil, fmt.Errorf("failed to marshal event: %w", err)
	}
	
	var lastErr error
	for attempt := 0; attempt <= c.retryCount; attempt++ {
		if attempt > 0 {
			select {
			case <-ctx.Done():
				return nil, ctx.Err()
			case <-time.After(c.retryDelay):
			}
		}
		
		req, err := http.NewRequestWithContext(ctx, "POST", url, bytes.NewBuffer(eventData))
		if err != nil {
			lastErr = fmt.Errorf("failed to create request: %w", err)
			continue
		}
		
		c.setHeaders(req)
		
		resp, err := c.httpClient.Do(req)
		if err != nil {
			lastErr = fmt.Errorf("failed to send request: %w", err)
			continue
		}
		
		defer resp.Body.Close()
		
		if c.isRetryableStatus(resp.StatusCode) && attempt < c.retryCount {
			lastErr = fmt.Errorf("retryable error: HTTP %d", resp.StatusCode)
			continue
		}
		
		body, err := io.ReadAll(resp.Body)
		if err != nil {
			return nil, fmt.Errorf("failed to read response body: %w", err)
		}
		
		var eventResp EventResponse
		if err := json.Unmarshal(body, &eventResp); err != nil {
			return nil, fmt.Errorf("failed to unmarshal response: %w", err)
		}
		
		if resp.StatusCode >= 400 {
			return &eventResp, fmt.Errorf("SLURP API error (HTTP %d): %s", resp.StatusCode, eventResp.Error)
		}
		
		return &eventResp, nil
	}
	
	return nil, fmt.Errorf("failed after %d attempts: %w", c.retryCount+1, lastErr)
}

// CreateEventsBatch sends multiple events to SLURP in a single request
func (c *SlurpClient) CreateEventsBatch(ctx context.Context, events []SlurpEvent) (*BatchEventResponse, error) {
	url := fmt.Sprintf("%s/api/events/batch", c.baseURL)
	
	batchRequest := BatchEventRequest{
		Events: events,
		Source: "bzzz-hmmm-integration",
	}
	
	batchData, err := json.Marshal(batchRequest)
	if err != nil {
		return nil, fmt.Errorf("failed to marshal batch request: %w", err)
	}
	
	var lastErr error
	for attempt := 0; attempt <= c.retryCount; attempt++ {
		if attempt > 0 {
			select {
			case <-ctx.Done():
				return nil, ctx.Err()
			case <-time.After(c.retryDelay):
			}
		}
		
		req, err := http.NewRequestWithContext(ctx, "POST", url, bytes.NewBuffer(batchData))
		if err != nil {
			lastErr = fmt.Errorf("failed to create batch request: %w", err)
			continue
		}
		
		c.setHeaders(req)
		
		resp, err := c.httpClient.Do(req)
		if err != nil {
			lastErr = fmt.Errorf("failed to send batch request: %w", err)
			continue
		}
		
		defer resp.Body.Close()
		
		if c.isRetryableStatus(resp.StatusCode) && attempt < c.retryCount {
			lastErr = fmt.Errorf("retryable error: HTTP %d", resp.StatusCode)
			continue
		}
		
		body, err := io.ReadAll(resp.Body)
		if err != nil {
			return nil, fmt.Errorf("failed to read batch response body: %w", err)
		}
		
		var batchResp BatchEventResponse
		if err := json.Unmarshal(body, &batchResp); err != nil {
			return nil, fmt.Errorf("failed to unmarshal batch response: %w", err)
		}
		
		if resp.StatusCode >= 400 {
			return &batchResp, fmt.Errorf("SLURP batch API error (HTTP %d): %s", resp.StatusCode, batchResp.Message)
		}
		
		return &batchResp, nil
	}
	
	return nil, fmt.Errorf("batch failed after %d attempts: %w", c.retryCount+1, lastErr)
}

// GetHealth checks SLURP service health
func (c *SlurpClient) GetHealth(ctx context.Context) (*HealthResponse, error) {
	url := fmt.Sprintf("%s/api/health", c.baseURL)
	
	req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
	if err != nil {
		return nil, fmt.Errorf("failed to create health request: %w", err)
	}
	
	c.setHeaders(req)
	
	resp, err := c.httpClient.Do(req)
	if err != nil {
		return nil, fmt.Errorf("failed to send health request: %w", err)
	}
	defer resp.Body.Close()
	
	body, err := io.ReadAll(resp.Body)
	if err != nil {
		return nil, fmt.Errorf("failed to read health response: %w", err)
	}
	
	var healthResp HealthResponse
	if err := json.Unmarshal(body, &healthResp); err != nil {
		return nil, fmt.Errorf("failed to unmarshal health response: %w", err)
	}
	
	if resp.StatusCode >= 400 {
		return &healthResp, fmt.Errorf("SLURP health check failed (HTTP %d)", resp.StatusCode)
	}
	
	return &healthResp, nil
}

// QueryEvents retrieves events from SLURP based on filters
func (c *SlurpClient) QueryEvents(ctx context.Context, filters map[string]string) ([]SlurpEvent, error) {
	baseURL := fmt.Sprintf("%s/api/events", c.baseURL)
	
	// Build query parameters
	params := url.Values{}
	for key, value := range filters {
		params.Add(key, value)
	}
	
	queryURL := baseURL
	if len(params) > 0 {
		queryURL = fmt.Sprintf("%s?%s", baseURL, params.Encode())
	}
	
	req, err := http.NewRequestWithContext(ctx, "GET", queryURL, nil)
	if err != nil {
		return nil, fmt.Errorf("failed to create query request: %w", err)
	}
	
	c.setHeaders(req)
	
	resp, err := c.httpClient.Do(req)
	if err != nil {
		return nil, fmt.Errorf("failed to send query request: %w", err)
	}
	defer resp.Body.Close()
	
	body, err := io.ReadAll(resp.Body)
	if err != nil {
		return nil, fmt.Errorf("failed to read query response: %w", err)
	}
	
	var events []SlurpEvent
	if err := json.Unmarshal(body, &events); err != nil {
		return nil, fmt.Errorf("failed to unmarshal events: %w", err)
	}
	
	if resp.StatusCode >= 400 {
		return nil, fmt.Errorf("SLURP query failed (HTTP %d)", resp.StatusCode)
	}
	
	return events, nil
}

// setHeaders sets common HTTP headers for SLURP API requests
func (c *SlurpClient) setHeaders(req *http.Request) {
	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("Accept", "application/json")
	req.Header.Set("User-Agent", "Bzzz-HMMM-Integration/1.0")
	
	if c.apiKey != "" {
		req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", c.apiKey))
	}
}

// isRetryableStatus determines if an HTTP status code is retryable
func (c *SlurpClient) isRetryableStatus(statusCode int) bool {
	switch statusCode {
	case http.StatusTooManyRequests,     // 429
		 http.StatusInternalServerError,  // 500
		 http.StatusBadGateway,          // 502
		 http.StatusServiceUnavailable,  // 503
		 http.StatusGatewayTimeout:      // 504
		return true
	default:
		return false
	}
}

// Close cleans up the client resources
func (c *SlurpClient) Close() error {
	// HTTP client doesn't need explicit cleanup, but we can implement
	// connection pooling cleanup if needed in the future
	return nil
}

// ValidateConnection tests the connection to SLURP
func (c *SlurpClient) ValidateConnection(ctx context.Context) error {
	_, err := c.GetHealth(ctx)
	return err
}