// Package openapi serves the embedded Swagger 2.0 specification at
// GET /api/v1/openapi.json.
//
// The embedded swagger.json in this directory is a copy of
// docs/swagger.json, which is generated by swaggo/swag from handler
// annotations (see cmd/api/main.go for the general API annotations).
// docs/swagger.json stays the source of truth; after re-running
// `swag init`, the copy here MUST be synced again, either manually:
//
//	cp docs/swagger.json internal/handler/openapi/swagger.json
//
// or via `go generate ./internal/handler/openapi` from the backend root.
package openapi

import (
	"crypto/sha256"
	_ "embed"
	"fmt"
	"net/http"

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

// Regenerate docs/swagger.json from handler annotations, then sync the
// embedded copy. Run from the backend root: go generate ./internal/handler/openapi
//
//go:generate sh -c "cd ../../.. && swag init -g cmd/api/main.go -o docs && cp docs/swagger.json internal/handler/openapi/swagger.json"

// spec is the embedded Swagger 2.0 document (copy of docs/swagger.json).
//
//go:embed swagger.json
var spec []byte

// specETag is a strong ETag derived from the embedded document; it changes
// only when the binary is rebuilt with a different spec.
var specETag = fmt.Sprintf("\"%x\"", sha256.Sum256(spec))

type Handler struct{}

func NewHandler() *Handler {
	return &Handler{}
}

// GetSpec serves the embedded Swagger document verbatim. The spec is not
// wrapped in the APIResponse envelope: OpenAPI tooling expects the raw
// document. Cache-Control: no-cache forces revalidation, and the ETag lets
// well-behaved clients get a cheap 304 when nothing changed.
func (h *Handler) GetSpec(c *gin.Context) {
	c.Header("Cache-Control", "no-cache")
	c.Header("ETag", specETag)
	if c.GetHeader("If-None-Match") == specETag {
		c.Status(http.StatusNotModified)
		return
	}
	c.Data(http.StatusOK, "application/json; charset=utf-8", spec)
}
