// Copyright 2026 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT

package v2

import (
	"net/http"

	"code.gitea.io/gitea/modules/health"
	"code.gitea.io/gitea/services/context"
)

// HealthCheckResponse represents the health check response
type HealthCheckResponse struct {
	Status     string                      `json:"status"`
	Version    string                      `json:"version"`
	Uptime     float64                     `json:"uptime_seconds"`
	Timestamp  string                      `json:"timestamp"`
	Components map[string]*ComponentStatus `json:"components"`
	System     *health.SystemInfo          `json:"system,omitempty"`
}

// ComponentStatus represents a component's health status
type ComponentStatus struct {
	Name        string         `json:"name"`
	Status      string         `json:"status"`
	Message     string         `json:"message,omitempty"`
	DurationMs  float64        `json:"duration_ms"`
	LastChecked string         `json:"last_checked"`
	Metadata    map[string]any `json:"metadata,omitempty"`
}

// HealthCheck performs a comprehensive health check
// @Summary Health check
// @Description Returns the health status of all components
// @Tags health
// @Produce json
// @Param detailed query bool false "Include system information"
// @Success 200 {object} HealthCheckResponse
// @Router /health [get]
func HealthCheck(ctx *context.APIContext) {
	includeSystem := ctx.FormBool("detailed")
	manager := health.GetManager()

	result := manager.Check(ctx.Req.Context(), includeSystem)

	// Convert to response format
	components := make(map[string]*ComponentStatus)
	for name, check := range result.Components {
		components[name] = &ComponentStatus{
			Name:        check.Name,
			Status:      string(check.Status),
			Message:     check.Message,
			DurationMs:  float64(check.Duration.Milliseconds()),
			LastChecked: check.LastChecked.Format("2006-01-02T15:04:05Z07:00"),
			Metadata:    check.Metadata,
		}
	}

	response := HealthCheckResponse{
		Status:     string(result.Status),
		Version:    result.Version,
		Uptime:     result.Uptime.Seconds(),
		Timestamp:  result.Timestamp.Format("2006-01-02T15:04:05Z07:00"),
		Components: components,
		System:     result.System,
	}

	// Set status code based on health
	var statusCode int
	switch result.Status {
	case health.StatusUnhealthy:
		statusCode = http.StatusServiceUnavailable
	case health.StatusDegraded:
		statusCode = http.StatusOK // Still OK but degraded
	default:
		statusCode = http.StatusOK
	}

	ctx.JSON(statusCode, response)
}

// LivenessCheck performs a simple liveness check
// @Summary Liveness probe
// @Description Returns OK if the service is running
// @Tags health
// @Produce json
// @Success 200 {object} map[string]any
// @Router /health/live [get]
func LivenessCheck(ctx *context.APIContext) {
	manager := health.GetManager()
	check := manager.LivenessCheck()

	ctx.JSON(http.StatusOK, map[string]any{
		"status":  string(check.Status),
		"message": check.Message,
	})
}

// ReadinessCheck performs a readiness check
// @Summary Readiness probe
// @Description Returns OK if the service is ready to handle requests
// @Tags health
// @Produce json
// @Success 200 {object} map[string]any
// @Success 503 {object} map[string]any
// @Router /health/ready [get]
func ReadinessCheck(ctx *context.APIContext) {
	manager := health.GetManager()
	result := manager.ReadinessCheck(ctx.Req.Context())

	response := map[string]any{
		"status":    string(result.Status),
		"timestamp": result.Timestamp.Format("2006-01-02T15:04:05Z07:00"),
	}

	if result.Status == health.StatusUnhealthy {
		ctx.JSON(http.StatusServiceUnavailable, response)
		return
	}

	ctx.JSON(http.StatusOK, response)
}

// ComponentHealthCheck checks a specific component
// @Summary Component health check
// @Description Returns the health status of a specific component
// @Tags health
// @Produce json
// @Param component path string true "Component name"
// @Success 200 {object} ComponentStatus
// @Failure 404 {object} map[string]any
// @Router /health/component/{component} [get]
func ComponentHealthCheck(ctx *context.APIContext) {
	component := ctx.PathParam("component")

	manager := health.GetManager()
	check, found := manager.CheckSingle(ctx.Req.Context(), component)

	if !found {
		ctx.JSON(http.StatusNotFound, map[string]any{
			"code":      "COMPONENT_NOT_FOUND",
			"message":   "Health check component not found",
			"component": component,
		})
		return
	}

	response := ComponentStatus{
		Name:        check.Name,
		Status:      string(check.Status),
		Message:     check.Message,
		DurationMs:  float64(check.Duration.Milliseconds()),
		LastChecked: check.LastChecked.Format("2006-01-02T15:04:05Z07:00"),
		Metadata:    check.Metadata,
	}

	statusCode := http.StatusOK
	if check.Status == health.StatusUnhealthy {
		statusCode = http.StatusServiceUnavailable
	}

	ctx.JSON(statusCode, response)
}
