package handlers

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

	"github.com/gin-gonic/gin"
	"github.com/redis/go-redis/v9"
	"go.uber.org/zap"
	"gorm.io/gorm"

	"veza-backend-api/internal/database"
	"veza-backend-api/internal/monitoring"
)

var (
	// startTime tracks when the server started
	startTime = time.Now()
)

// StatusResponse représente la réponse complète du status endpoint
type StatusResponse struct {
	Status      string                 `json:"status"`
	UptimeSec   int64                  `json:"uptime_seconds"`
	Services    map[string]ServiceInfo `json:"services"`
	Version     string                 `json:"version"`
	GitCommit   string                 `json:"git_commit"`
	BuildTime   string                 `json:"build_time"`
	Environment string                 `json:"environment,omitempty"`
}

// ServiceInfo représente l'état d'un service
type ServiceInfo struct {
	Status  string  `json:"status"`
	Latency float64 `json:"latency_ms,omitempty"`
	Message string  `json:"message,omitempty"`
}

// StatusHandler gère les endpoints de status
type StatusHandler struct {
	db              *gorm.DB
	logger          *zap.Logger
	redis           *redis.Client
	chatServerURL   string
	streamServerURL string
	version         string
	gitCommit       string
	buildTime       string
	environment     string
}

// NewStatusHandler crée un nouveau handler de status
func NewStatusHandler(
	db *gorm.DB,
	logger *zap.Logger,
	redisClient interface{},
	chatServerURL string,
	streamServerURL string,
	version string,
	gitCommit string,
	buildTime string,
	environment string,
) *StatusHandler {
	h := &StatusHandler{
		db:              db,
		logger:          logger,
		chatServerURL:   chatServerURL,
		streamServerURL: streamServerURL,
		version:         version,
		gitCommit:       gitCommit,
		buildTime:       buildTime,
		environment:     environment,
	}

	// Type assertion for Redis
	if r, ok := redisClient.(*redis.Client); ok {
		h.redis = r
	}

	return h
}

// GetStatus retourne le status complet de l'application
func (h *StatusHandler) GetStatus(c *gin.Context) {
	response := StatusResponse{
		Status:    "ok",
		UptimeSec: int64(time.Since(startTime).Seconds()),
		Services:  make(map[string]ServiceInfo),
		Version:   h.version,
		GitCommit: h.gitCommit,
		BuildTime: h.buildTime,
	}

	if h.environment != "" {
		response.Environment = h.environment
	}

	// Check database
	dbInfo := h.checkDatabase()
	response.Services["database"] = dbInfo

	// Check Redis
	redisInfo := h.checkRedis()
	response.Services["redis"] = redisInfo

	// Check chat server (if configured)
	if h.chatServerURL != "" {
		chatInfo := h.checkChatServer(c.Request.Context())
		response.Services["chat_server"] = chatInfo
	}

	// Check stream server (if configured)
	if h.streamServerURL != "" {
		streamInfo := h.checkStreamServer(c.Request.Context())
		response.Services["stream_server"] = streamInfo
	}

	// Déterminer le statut global
	globalStatus := "ok"
	for _, service := range response.Services {
		if service.Status == "error" {
			globalStatus = "degraded"
			break
		}
		if service.Status == "slow" {
			if globalStatus != "degraded" {
				globalStatus = "degraded"
			}
		}
	}
	response.Status = globalStatus

	statusCode := http.StatusOK
	if globalStatus == "degraded" {
		statusCode = http.StatusServiceUnavailable
	}

	RespondSuccess(c, statusCode, response)
}

// checkDatabase vérifie la connexion à la base de données
func (h *StatusHandler) checkDatabase() ServiceInfo {
	start := time.Now()

	err := database.IsConnectionHealthy(h.db, 5*time.Second)
	duration := time.Since(start)
	latencyMs := float64(duration.Nanoseconds()) / 1e6

	if err != nil {
		monitoring.RecordHealthCheck("database", latencyMs, "error")
		return ServiceInfo{
			Status:  "error",
			Message: err.Error(),
			Latency: latencyMs,
		}
	}

	status := "ok"
	if latencyMs > 100 {
		status = "slow"
	}

	monitoring.RecordHealthCheck("database", latencyMs, status)
	return ServiceInfo{
		Status:  status,
		Latency: latencyMs,
	}
}

// checkRedis vérifie la connexion à Redis
func (h *StatusHandler) checkRedis() ServiceInfo {
	start := time.Now()

	if h.redis == nil {
		monitoring.RecordHealthCheck("redis", 0, "error")
		return ServiceInfo{
			Status:  "error",
			Message: "Redis connection not configured",
		}
	}

	ctx, cancel := context.WithTimeout(context.Background(), 400*time.Millisecond)
	defer cancel()

	_, err := h.redis.Ping(ctx).Result()
	duration := time.Since(start)
	latencyMs := float64(duration.Nanoseconds()) / 1e6

	if err != nil {
		monitoring.RecordHealthCheck("redis", latencyMs, "error")
		return ServiceInfo{
			Status:  "error",
			Message: err.Error(),
			Latency: latencyMs,
		}
	}

	status := "ok"
	if latencyMs > 50 {
		status = "slow"
	}

	monitoring.RecordHealthCheck("redis", latencyMs, status)
	return ServiceInfo{
		Status:  status,
		Latency: latencyMs,
	}
}

// checkChatServer vérifie la disponibilité du chat server
func (h *StatusHandler) checkChatServer(ctx context.Context) ServiceInfo {
	start := time.Now()

	client := &http.Client{
		Timeout: 400 * time.Millisecond,
	}

	url := h.chatServerURL
	if url[len(url)-1] != '/' {
		url += "/"
	}
	url += "health"

	req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
	if err != nil {
		return ServiceInfo{
			Status:  "error",
			Message: err.Error(),
			Latency: 0,
		}
	}

	resp, err := client.Do(req)
	duration := time.Since(start)
	latencyMs := float64(duration.Nanoseconds()) / 1e6

	if err != nil {
		monitoring.RecordHealthCheck("chat_server", latencyMs, "error")
		return ServiceInfo{
			Status:  "error",
			Message: err.Error(),
			Latency: latencyMs,
		}
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		monitoring.RecordHealthCheck("chat_server", latencyMs, "error")
		return ServiceInfo{
			Status:  "error",
			Message: "chat server returned non-200 status",
			Latency: latencyMs,
		}
	}

	status := "ok"
	if latencyMs > 100 {
		status = "slow"
	}

	monitoring.RecordHealthCheck("chat_server", latencyMs, status)
	return ServiceInfo{
		Status:  status,
		Latency: latencyMs,
	}
}

// checkStreamServer vérifie la disponibilité du stream server
func (h *StatusHandler) checkStreamServer(ctx context.Context) ServiceInfo {
	start := time.Now()

	client := &http.Client{
		Timeout: 400 * time.Millisecond,
	}

	url := h.streamServerURL
	if url[len(url)-1] != '/' {
		url += "/"
	}
	url += "health"

	req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
	if err != nil {
		return ServiceInfo{
			Status:  "error",
			Message: err.Error(),
			Latency: 0,
		}
	}

	resp, err := client.Do(req)
	duration := time.Since(start)
	latencyMs := float64(duration.Nanoseconds()) / 1e6

	if err != nil {
		monitoring.RecordHealthCheck("stream_server", latencyMs, "error")
		return ServiceInfo{
			Status:  "error",
			Message: err.Error(),
			Latency: latencyMs,
		}
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		monitoring.RecordHealthCheck("stream_server", latencyMs, "error")
		return ServiceInfo{
			Status:  "error",
			Message: "stream server returned non-200 status",
			Latency: latencyMs,
		}
	}

	status := "ok"
	if latencyMs > 100 {
		status = "slow"
	}

	monitoring.RecordHealthCheck("stream_server", latencyMs, status)
	return ServiceInfo{
		Status:  status,
		Latency: latencyMs,
	}
}

// GetSystemInfo retourne des informations système (pour debug)
func (h *StatusHandler) GetSystemInfo(c *gin.Context) {
	var m runtime.MemStats
	runtime.ReadMemStats(&m)

	// Utiliser la fonction bToMb définie dans system_metrics.go
	bToMb := func(b uint64) uint64 {
		return b / 1024 / 1024
	}

	RespondSuccess(c, http.StatusOK, gin.H{
		"uptime_seconds": int64(time.Since(startTime).Seconds()),
		"memory": gin.H{
			"alloc_mb":       bToMb(m.Alloc),
			"total_alloc_mb": bToMb(m.TotalAlloc),
			"sys_mb":         bToMb(m.Sys),
			"num_gc":         m.NumGC,
		},
		"goroutines": runtime.NumGoroutine(),
	})
}
