package controllers

import (
	"context"
	"net/http"
	"runtime"
	"time"

	"fotbal-club/internal/services"

	"github.com/gin-gonic/gin"
	"gorm.io/gorm"
)

// HealthController handles health check endpoints
type HealthController struct {
	DB *gorm.DB
}

// HealthResponse represents health check response
type HealthResponse struct {
	Status      string                 `json:"status"`
	Timestamp   time.Time              `json:"timestamp"`
	Version     string                 `json:"version,omitempty"`
	Checks      map[string]CheckResult `json:"checks"`
	System      SystemInfo             `json:"system,omitempty"`
}

// CheckResult represents individual health check result
type CheckResult struct {
	Status    string        `json:"status"`
	Message   string        `json:"message,omitempty"`
	Latency   time.Duration `json:"latency_ms,omitempty"`
	Timestamp time.Time     `json:"timestamp"`
}

// SystemInfo represents system resource information
type SystemInfo struct {
	Goroutines   int    `json:"goroutines"`
	MemoryAlloc  uint64 `json:"memory_alloc_mb"`
	MemoryTotal  uint64 `json:"memory_total_mb"`
	MemorySys    uint64 `json:"memory_sys_mb"`
	NumCPU       int    `json:"num_cpu"`
	GoVersion    string `json:"go_version"`
}

// Liveness returns a simple liveness probe
func (hc *HealthController) Liveness(c *gin.Context) {
	c.JSON(http.StatusOK, gin.H{
		"status": "alive",
		"timestamp": time.Now(),
	})
}

// Readiness returns detailed readiness probe
func (hc *HealthController) Readiness(c *gin.Context) {
	ctx, cancel := context.WithTimeout(c.Request.Context(), 5*time.Second)
	defer cancel()

	checks := make(map[string]CheckResult)
	overallStatus := "healthy"

	// Check database
	dbCheck := hc.checkDatabase(ctx)
	checks["database"] = dbCheck
	if dbCheck.Status != "healthy" {
		overallStatus = "unhealthy"
	}

	// Check cache
	cacheCheck := hc.checkCache(ctx)
	checks["cache"] = cacheCheck
	if cacheCheck.Status != "degraded" && cacheCheck.Status != "healthy" {
		overallStatus = "unhealthy"
	}

	// Check disk space
	diskCheck := hc.checkDiskSpace()
	checks["disk"] = diskCheck
	if diskCheck.Status != "healthy" {
		overallStatus = "degraded"
	}

	response := HealthResponse{
		Status:    overallStatus,
		Timestamp: time.Now(),
		Checks:    checks,
	}

	statusCode := http.StatusOK
	if overallStatus == "unhealthy" {
		statusCode = http.StatusServiceUnavailable
	}

	c.JSON(statusCode, response)
}

// Health returns comprehensive health information
func (hc *HealthController) Health(c *gin.Context) {
	ctx, cancel := context.WithTimeout(c.Request.Context(), 10*time.Second)
	defer cancel()

	checks := make(map[string]CheckResult)
	overallStatus := "healthy"

	// All checks
	dbCheck := hc.checkDatabase(ctx)
	checks["database"] = dbCheck
	if dbCheck.Status != "healthy" {
		overallStatus = "unhealthy"
	}

	cacheCheck := hc.checkCache(ctx)
	checks["cache"] = cacheCheck

	diskCheck := hc.checkDiskSpace()
	checks["disk"] = diskCheck

	// System info
	sysInfo := hc.getSystemInfo()

	response := HealthResponse{
		Status:    overallStatus,
		Timestamp: time.Now(),
		Version:   "1.0.0", // Use actual version
		Checks:    checks,
		System:    sysInfo,
	}

	statusCode := http.StatusOK
	if overallStatus == "unhealthy" {
		statusCode = http.StatusServiceUnavailable
	}

	c.JSON(statusCode, response)
}

// checkDatabase verifies database connectivity
func (hc *HealthController) checkDatabase(ctx context.Context) CheckResult {
	start := time.Now()
	
	sqlDB, err := hc.DB.DB()
	if err != nil {
		return CheckResult{
			Status:    "unhealthy",
			Message:   "Cannot get database connection: " + err.Error(),
			Timestamp: time.Now(),
		}
	}

	err = sqlDB.PingContext(ctx)
	latency := time.Since(start)

	if err != nil {
		return CheckResult{
			Status:    "unhealthy",
			Message:   "Database ping failed: " + err.Error(),
			Latency:   latency,
			Timestamp: time.Now(),
		}
	}

	// Check connection pool stats
	stats := sqlDB.Stats()
	if stats.OpenConnections >= stats.MaxOpenConnections {
		return CheckResult{
			Status:    "degraded",
			Message:   "Connection pool at maximum capacity",
			Latency:   latency,
			Timestamp: time.Now(),
		}
	}

	return CheckResult{
		Status:    "healthy",
		Message:   "Database connection successful",
		Latency:   latency,
		Timestamp: time.Now(),
	}
}

// checkCache verifies cache service
func (hc *HealthController) checkCache(ctx context.Context) CheckResult {
	start := time.Now()
	cache := services.GetCacheService()

	// Test cache write/read
	testKey := "health:check"
	testValue := "ok"
	
	err := cache.Set(testKey, testValue, 1*time.Minute)
	if err != nil {
		return CheckResult{
			Status:    "unhealthy",
			Message:   "Cache write failed: " + err.Error(),
			Latency:   time.Since(start),
			Timestamp: time.Now(),
		}
	}

	var result string
	err = cache.Get(testKey, &result)
	if err != nil {
		return CheckResult{
			Status:    "unhealthy",
			Message:   "Cache read failed: " + err.Error(),
			Latency:   time.Since(start),
			Timestamp: time.Now(),
		}
	}

	cache.Delete(testKey)

	return CheckResult{
		Status:    "healthy",
		Message:   "Cache operational",
		Latency:   time.Since(start),
		Timestamp: time.Now(),
	}
}

// checkDiskSpace checks available disk space
func (hc *HealthController) checkDiskSpace() CheckResult {
	// This is a simplified check
	// In production, implement proper disk space monitoring
	
	return CheckResult{
		Status:    "healthy",
		Message:   "Disk space sufficient",
		Timestamp: time.Now(),
	}
}

// getSystemInfo collects system resource information
func (hc *HealthController) getSystemInfo() SystemInfo {
	var m runtime.MemStats
	runtime.ReadMemStats(&m)

	return SystemInfo{
		Goroutines:   runtime.NumGoroutine(),
		MemoryAlloc:  m.Alloc / 1024 / 1024,       // MB
		MemoryTotal:  m.TotalAlloc / 1024 / 1024,  // MB
		MemorySys:    m.Sys / 1024 / 1024,         // MB
		NumCPU:       runtime.NumCPU(),
		GoVersion:    runtime.Version(),
	}
}

// Metrics returns Prometheus-compatible metrics
func (hc *HealthController) Metrics(c *gin.Context) {
	var m runtime.MemStats
	runtime.ReadMemStats(&m)

	// Simple text-based metrics
	metrics := ""
	metrics += "# HELP go_goroutines Number of goroutines\n"
	metrics += "# TYPE go_goroutines gauge\n"
	metrics += "go_goroutines " + string(rune(runtime.NumGoroutine())) + "\n\n"

	metrics += "# HELP go_memory_alloc_bytes Allocated memory in bytes\n"
	metrics += "# TYPE go_memory_alloc_bytes gauge\n"
	metrics += "go_memory_alloc_bytes " + string(rune(m.Alloc)) + "\n\n"

	c.String(http.StatusOK, metrics)
}
