// Package router implements model-based provider routing and RBAC for the Veylant proxy.
package router

import (
	"strings"

	"github.com/veylant/ia-gateway/internal/apierror"
	"github.com/veylant/ia-gateway/internal/config"
)

// Canonical role names as defined in Keycloak.
const (
	roleAdmin   = "admin"
	roleManager = "manager"
	roleUser    = "user"
	roleAuditor = "auditor"
)

// HasAccess returns nil if a user holding roles may use model, or a 403 error.
//
// Rules (evaluated top to bottom):
//   - admin / manager → unrestricted access to all models
//   - auditor         → 403 unless cfg.AuditorCanComplete is true
//   - user            → allowed only if model matches cfg.UserAllowedModels
//     (exact or prefix, e.g. "gpt-4o-mini" matches "gpt-4o-mini-2024-07-18")
//   - unknown role    → treated as user (fail-safe)
func HasAccess(roles []string, model string, cfg *config.RBACConfig) error {
	if hasRole(roles, roleAdmin) || hasRole(roles, roleManager) {
		return nil
	}
	if hasRole(roles, roleAuditor) {
		if cfg.AuditorCanComplete {
			return nil
		}
		return apierror.NewForbiddenError("auditors are not permitted to make completion requests")
	}
	// User role (or any unknown role treated as user for fail-safe behaviour).
	if modelMatchesAny(model, cfg.UserAllowedModels) {
		return nil
	}
	allowed := strings.Join(cfg.UserAllowedModels, ", ")
	return apierror.NewForbiddenError(
		"model \"" + model + "\" is not available for your role — allowed models for your role: [" + allowed + "]. Contact your administrator to request access.",
	)
}

// hasRole reports whether role appears in roles (case-insensitive).
func hasRole(roles []string, role string) bool {
	for _, r := range roles {
		if strings.EqualFold(r, role) {
			return true
		}
	}
	return false
}

// modelMatchesAny returns true if model equals any pattern or starts with any pattern.
// This allows exact matches ("gpt-4o-mini") and prefix matches
// ("gpt-4o-mini" matches "gpt-4o-mini-2024-07-18").
func modelMatchesAny(model string, patterns []string) bool {
	for _, p := range patterns {
		if model == p || strings.HasPrefix(model, p) {
			return true
		}
	}
	return false
}
