package authz

import rego.v1

# ─── Data ─────────────────────────────────────────────────────────────────────
# data.endpoint_permissions is loaded from policies/data/scopes.json
# Structure: { "METHOD:/path/pattern": ["scope1", ...], ... }

# ─── Default ──────────────────────────────────────────────────────────────────
default allow := false

default reason := "insufficient_scope"

# ─── Path pattern normalisation ───────────────────────────────────────────────
# Converts a concrete request path to a pattern key by replacing UUID-like
# segments with named placeholders.
#
# Supported patterns (longest-match wins via iteration):
#   /api/v1/agents/{uuid}/credentials/{uuid}/rotate
#   /api/v1/agents/{uuid}/credentials/{uuid}
#   /api/v1/agents/{uuid}/credentials
#   /api/v1/agents/{uuid}
#   /api/v1/agents
#   /api/v1/token/introspect
#   /api/v1/token/revoke
#   /api/v1/audit/{uuid}
#   /api/v1/audit

# Build the lookup key from method + normalised path.
lookup_key(method, path) := key if {
    normalised := normalise_path(path)
    key := concat(":", [method, normalised])
}

# Normalise a concrete path to its pattern form.
normalise_path(path) := "/api/v1/agents/:id/credentials/:credId/rotate" if {
    regex.match(`^/api/v1/agents/[^/]+/credentials/[^/]+/rotate$`, path)
}

normalise_path(path) := "/api/v1/agents/:id/credentials/:credId" if {
    regex.match(`^/api/v1/agents/[^/]+/credentials/[^/]+$`, path)
}

normalise_path(path) := "/api/v1/agents/:id/credentials" if {
    regex.match(`^/api/v1/agents/[^/]+/credentials$`, path)
}

normalise_path(path) := "/api/v1/agents/:id" if {
    regex.match(`^/api/v1/agents/[^/]+$`, path)
}

normalise_path(path) := "/api/v1/agents" if {
    path == "/api/v1/agents"
}

normalise_path(path) := "/api/v1/token/introspect" if {
    path == "/api/v1/token/introspect"
}

normalise_path(path) := "/api/v1/token/revoke" if {
    path == "/api/v1/token/revoke"
}

normalise_path(path) := "/api/v1/audit/:id" if {
    regex.match(`^/api/v1/audit/[^/]+$`, path)
}

normalise_path(path) := "/api/v1/audit" if {
    path == "/api/v1/audit"
}

normalise_path(path) := "/api/v1/organizations/:id/members" if {
    regex.match(`^/api/v1/organizations/[^/]+/members$`, path)
}

normalise_path(path) := "/api/v1/organizations/:id" if {
    regex.match(`^/api/v1/organizations/[^/]+$`, path)
}

normalise_path(path) := "/api/v1/organizations" if {
    path == "/api/v1/organizations"
}

normalise_path(path) := "/api/v1/federation/partners/:id" if {
    regex.match(`^/api/v1/federation/partners/[^/]+$`, path)
}

normalise_path(path) := "/api/v1/federation/partners" if {
    path == "/api/v1/federation/partners"
}

normalise_path(path) := "/api/v1/federation/trust" if {
    path == "/api/v1/federation/trust"
}

# ─── Core allow rule ──────────────────────────────────────────────────────────
# allow = true if every required scope for the endpoint is present in input.scopes.

allow if {
    key := lookup_key(input.method, input.path)
    required := data.endpoint_permissions[key]
    every req_scope in required {
        req_scope in input.scopes
    }
}

# reason is populated only on deny.
reason := "missing required scope for this endpoint" if {
    not allow
}
