package meta

import (
	"cmp"
	"context"
	"slices"

	"nadir/internal/module"

	"github.com/danielgtaylor/huma/v2"
)

// ModuleInfo describes a registered module: its stable ID, display name, and
// the permissions it exposes. The frontend uses this to drive navigation and
// render the role/permission matrix.
type ModuleInfo struct {
	ID          string   `json:"id" example:"system" doc:"Stable module identifier"`
	Name        string   `json:"name" example:"System" doc:"Human-readable module name"`
	Permissions []string `json:"permissions" doc:"Permissions this module exposes (never includes the \"*\" wildcard)"`
}

type ModulesOutput struct {
	Body struct {
		Modules []ModuleInfo `json:"modules" doc:"Registered modules, sorted by ID"`
	}
}

// Register adds the read-only module-discovery endpoint. It is intentionally
// public: it exposes only the API's static shape (module IDs and permission
// vocabulary), the same information already served by /openapi.json. The module
// list is fixed at startup, so the response is computed once here.
func Register(api huma.API, mods []module.Module) {
	infos := make([]ModuleInfo, 0, len(mods))
	for _, m := range mods {
		perms := m.Permissions()
		ps := make([]string, len(perms))
		for i, p := range perms {
			ps[i] = string(p)
		}
		infos = append(infos, ModuleInfo{ID: m.ID(), Name: module.Title(m.ID()), Permissions: ps})
	}
	slices.SortFunc(infos, func(a, b ModuleInfo) int { return cmp.Compare(a.ID, b.ID) })

	huma.Register(api, huma.Operation{
		OperationID: "list-modules",
		Method:      "GET",
		Path:        "/api/_modules",
		Summary:     "List registered modules",
		Description: "Returns every registered module with its ID, display name, " +
			"and exported permissions. Public (same static shape as /openapi.json); " +
			"used by the frontend for navigation and the role/permission matrix.",
		Tags: []string{"Meta"},
	}, func(ctx context.Context, _ *struct{}) (*ModulesOutput, error) {
		out := &ModulesOutput{}
		out.Body.Modules = infos
		return out, nil
	})
}
