/**
 * @sanity/ailf-core — Shared team-resolution helper + registration error builder.
 *
 * External task registration (mirroring a cross-repo task suite into Content
 * Lake) is gated on the caller's `owner.team` resolving to an *active*
 * `ailf.team`. Two server surfaces enforce that gate — the API gateway
 * pre-flight (`packages/api`, synchronous HTTP 422) and the pipeline mirror
 * step (`packages/eval`, fail-fast abort) — and both must produce an identical,
 * caller-actionable error. To keep them from drifting, the team lookup and the
 * error wording live here, in one place both import.
 *
 * Why core (not eval): `packages/api` cannot import from `@sanity/ailf` (the
 * eval package) per the api-gateway rule, but it can depend on
 * `@sanity/ailf-core`. This is the same engines-in-core rationale as D0048.
 *
 * `resolveActiveTeam` performs I/O, so it takes the Sanity client as a
 * parameter (the gateway and the pipeline each pass their own, configured for
 * the `ailf-prod-private` dataset). The parameter is typed structurally
 * ({@link TeamLookupClient}) rather than as `@sanity/client`'s `SanityClient`,
 * so this package stays the dependency-graph leaf it is meant to be (it depends
 * only on `@sanity/ailf-shared` and `zod`). A real `SanityClient` satisfies the
 * interface; a test fake does too.
 *
 * @see docs/superpowers/specs/2026-06-15-external-task-registration-design.md (§6)
 * @see docs/decisions/D0055-team-entity-ownership-subscription-split.md
 * @see packages/shared/src/registration.ts — the cross-cut constants
 */
import { normalizeOwnerTeam, REGISTRATION_REMEDIATION, } from "../../ailf-shared/index.js";
import { z } from "zod";
/**
 * Projection of an `ailf.team` document, narrowed to the fields the gate needs.
 * Parsed defensively: a row missing a field or carrying an unexpected status is
 * rejected (→ treated as `absent`), so a malformed team can never be mistaken
 * for an active one.
 */
const TeamLookupRowSchema = z.object({
    teamId: z.string().min(1),
    slug: z.string().min(1),
    status: z.enum(["active", "archived"]),
});
/**
 * Select a single non-draft `ailf.team` by slug, regardless of status.
 * Filtering on `status == "active"` in GROQ would collapse "archived" and
 * "absent" into one empty result; resolving the status in code instead keeps
 * the two distinguishable. `[0]` returns the first match or `null`.
 */
const TEAM_BY_SLUG_QUERY = /* groq */ `
  *[_type == "ailf.team" && slug.current == $slug && !(_id in path("drafts.**"))][0]{
    "teamId": _id,
    "slug": slug.current,
    status
  }
`;
/**
 * Resolve an `owner.team` slug to an active / archived / absent verdict.
 *
 * The slug is normalized first (trim, lowercase, alias fold via
 * {@link normalizeOwnerTeam}), so `core-docs` resolves the `docs` team. An
 * empty or `"unknown"` slug short-circuits to `absent` without a query — there
 * is no team to find. A row that fails {@link TeamLookupRowSchema} is treated
 * as `absent` (conservative: never register against a malformed team).
 */
export async function resolveActiveTeam(slug, client) {
    const normalized = normalizeOwnerTeam(slug);
    if (normalized === "unknown")
        return { status: "absent", slug: normalized };
    const raw = await client.fetch(TEAM_BY_SLUG_QUERY, { slug: normalized });
    const parsed = TeamLookupRowSchema.safeParse(raw);
    if (!parsed.success)
        return { status: "absent", slug: normalized };
    const { teamId, slug: resolvedSlug, status } = parsed.data;
    return { status, teamId, slug: resolvedSlug };
}
function registrationMessage(input) {
    switch (input.code) {
        case "registration_team_not_found":
            return `registerTasks was requested but owner.team "${input.team}" is not an active AILF team, so the tasks can't be attributed and were not registered.`;
        case "registration_no_tasks":
            return `registerTasks was requested for owner.team "${input.team}" but no tasks were provided, so there is nothing to register.`;
        case "registration_area_team_conflict":
            return `registerTasks was requested for owner.team "${input.team}", but the feature area "${input.area}" already belongs to team "${input.existingTeam}"; registering would mis-attribute it.`;
    }
}
/**
 * Build the structured error for a refused registration. The single source of
 * truth for registration failure wording — both the gateway pre-flight and the
 * pipeline mirror step call this, so the caller sees the same `code`, `message`,
 * and `remediation` whichever surface refuses.
 */
export function buildRegistrationError(input) {
    return {
        code: input.code,
        message: registrationMessage(input),
        remediation: REGISTRATION_REMEDIATION,
        team: input.team,
    };
}
