package blossom

import (
	"context"
	"fmt"
	"log"
	"net/http"
	"sync"
	"sync/atomic"
	"time"
)

// BlossomPool manages a pool of Blossom server connections with load balancing
type BlossomPool struct {
	servers     []PooledClient
	healthMutex sync.RWMutex
	roundRobin  uint64
	config      *PoolConfig
}

// PooledClient wraps a Blossom client with health status
type PooledClient struct {
	client    *Client
	serverURL string
	healthy   bool
	lastCheck time.Time
	failures  int
	mutex     sync.RWMutex
}

// PoolConfig configures the connection pool
type PoolConfig struct {
	HealthCheckInterval time.Duration
	HealthCheckTimeout  time.Duration
	MaxFailures         int
	RetryDelay          time.Duration
	LoadBalanceMethod   string // "round_robin", "least_connections", "health_weighted"
}

// NewBlossomPool creates a new connection pool for Blossom servers
func NewBlossomPool(serverURLs []string, config *PoolConfig) (*BlossomPool, error) {
	if len(serverURLs) == 0 {
		return nil, fmt.Errorf("no Blossom servers provided")
	}

	if config == nil {
		config = &PoolConfig{
			HealthCheckInterval: 30 * time.Second,
			HealthCheckTimeout:  5 * time.Second,
			MaxFailures:         3,
			RetryDelay:          10 * time.Second,
			LoadBalanceMethod:   "round_robin",
		}
	}

	pool := &BlossomPool{
		servers: make([]PooledClient, len(serverURLs)),
		config:  config,
	}

	// Initialize clients
	for i, serverURL := range serverURLs {
		client := NewClient(serverURL)
		pool.servers[i] = PooledClient{
			client:    client,
			serverURL: serverURL,
			healthy:   true, // Assume healthy initially
			lastCheck: time.Now(),
		}
	}

	// Start health check routine
	go pool.healthCheckRoutine()

	return pool, nil
}

// GetClient returns a healthy client using load balancing
func (p *BlossomPool) GetClient() *Client {
	p.healthMutex.RLock()
	defer p.healthMutex.RUnlock()

	// Get healthy servers
	var healthyServers []int
	for i := range p.servers {
		p.servers[i].mutex.RLock()
		if p.servers[i].healthy {
			healthyServers = append(healthyServers, i)
		}
		p.servers[i].mutex.RUnlock()
	}

	if len(healthyServers) == 0 {
		log.Printf("Warning: No healthy Blossom servers available, using first server")
		return p.servers[0].client
	}

	// Load balance among healthy servers
	switch p.config.LoadBalanceMethod {
	case "round_robin":
		idx := atomic.AddUint64(&p.roundRobin, 1) % uint64(len(healthyServers))
		return p.servers[healthyServers[idx]].client
	default:
		// Default to round robin
		idx := atomic.AddUint64(&p.roundRobin, 1) % uint64(len(healthyServers))
		return p.servers[healthyServers[idx]].client
	}
}

// healthCheckRoutine periodically checks server health
func (p *BlossomPool) healthCheckRoutine() {
	ticker := time.NewTicker(p.config.HealthCheckInterval)
	defer ticker.Stop()

	for range ticker.C {
		p.checkAllServers()
	}
}

// checkAllServers performs health checks on all servers
func (p *BlossomPool) checkAllServers() {
	var wg sync.WaitGroup
	
	for i := range p.servers {
		wg.Add(1)
		go func(idx int) {
			defer wg.Done()
			p.checkServerHealth(idx)
		}(i)
	}
	
	wg.Wait()
}

// checkServerHealth checks if a specific server is healthy
func (p *BlossomPool) checkServerHealth(idx int) {
	server := &p.servers[idx]
	
	ctx, cancel := context.WithTimeout(context.Background(), p.config.HealthCheckTimeout)
	defer cancel()

	// Simple health check - try to get server info
	req, err := http.NewRequestWithContext(ctx, "GET", server.serverURL+"/health", nil)
	if err != nil {
		p.markServerUnhealthy(idx, err)
		return
	}

	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		p.markServerUnhealthy(idx, err)
		return
	}
	defer resp.Body.Close()

	if resp.StatusCode == http.StatusOK {
		p.markServerHealthy(idx)
	} else {
		p.markServerUnhealthy(idx, fmt.Errorf("health check returned status %d", resp.StatusCode))
	}
}

// markServerHealthy marks a server as healthy
func (p *BlossomPool) markServerHealthy(idx int) {
	server := &p.servers[idx]
	server.mutex.Lock()
	defer server.mutex.Unlock()

	if !server.healthy {
		log.Printf("Blossom server %s is now healthy", server.serverURL)
	}
	
	server.healthy = true
	server.failures = 0
	server.lastCheck = time.Now()
}

// markServerUnhealthy marks a server as unhealthy
func (p *BlossomPool) markServerUnhealthy(idx int, err error) {
	server := &p.servers[idx]
	server.mutex.Lock()
	defer server.mutex.Unlock()

	server.failures++
	server.lastCheck = time.Now()

	if server.failures >= p.config.MaxFailures {
		if server.healthy {
			log.Printf("Blossom server %s marked unhealthy after %d failures: %v", 
				server.serverURL, server.failures, err)
		}
		server.healthy = false
	}
}

// GetHealthyServerCount returns the number of healthy servers
func (p *BlossomPool) GetHealthyServerCount() int {
	p.healthMutex.RLock()
	defer p.healthMutex.RUnlock()

	count := 0
	for i := range p.servers {
		p.servers[i].mutex.RLock()
		if p.servers[i].healthy {
			count++
		}
		p.servers[i].mutex.RUnlock()
	}
	return count
}

// GetServerStatus returns status of all servers
func (p *BlossomPool) GetServerStatus() []map[string]interface{} {
	p.healthMutex.RLock()
	defer p.healthMutex.RUnlock()

	status := make([]map[string]interface{}, len(p.servers))
	for i, server := range p.servers {
		server.mutex.RLock()
		status[i] = map[string]interface{}{
			"url":        server.serverURL,
			"healthy":    server.healthy,
			"failures":   server.failures,
			"last_check": server.lastCheck,
		}
		server.mutex.RUnlock()
	}
	return status
}