import { getSpiceDbConfig } from "./config.js";
import { v1 } from "@authzed/authzed-node";

//#region src/auth/authz/client.ts
/**
* SpiceDB Client Wrapper
*
* Provides a singleton SpiceDB client and helper functions for common operations.
*/
const { RelationshipUpdate_Operation, CheckPermissionResponse_Permissionship } = v1;
let client = null;
/**
* Get the SpiceDB client singleton.
* Creates a new client on first call.
*/
function getSpiceClient() {
	if (!client) {
		const config = getSpiceDbConfig();
		client = v1.NewClient(config.token, config.endpoint, config.tlsEnabled ? v1.ClientSecurity.SECURE : v1.ClientSecurity.INSECURE_PLAINTEXT_CREDENTIALS);
	}
	return client;
}
/**
* Reset the client (useful for testing)
*/
function resetSpiceClient() {
	client = null;
}
const RelationshipOperation = RelationshipUpdate_Operation;
const Permissionship = CheckPermissionResponse_Permissionship;
/**
* Check if a subject has a permission on a resource.
*/
async function checkPermission(params) {
	return (await getSpiceClient().promises.checkPermission({
		resource: {
			objectType: params.resourceType,
			objectId: params.resourceId
		},
		permission: params.permission,
		subject: {
			object: {
				objectType: params.subjectType,
				objectId: params.subjectId
			},
			optionalRelation: ""
		},
		consistency: { requirement: {
			oneofKind: "fullyConsistent",
			fullyConsistent: true
		} },
		context: void 0,
		withTracing: false
	})).permissionship === CheckPermissionResponse_Permissionship.HAS_PERMISSION;
}
/**
* Check multiple permissions on a resource in a single request.
* More efficient than multiple checkPermission calls.
*
* @returns Record mapping permission names to boolean results
*/
async function checkBulkPermissions(params) {
	const spice = getSpiceClient();
	const items = params.permissions.map((permission) => v1.CheckBulkPermissionsRequestItem.create({
		resource: v1.ObjectReference.create({
			objectType: params.resourceType,
			objectId: params.resourceId
		}),
		permission,
		subject: v1.SubjectReference.create({ object: v1.ObjectReference.create({
			objectType: params.subjectType,
			objectId: params.subjectId
		}) })
	}));
	const response = await spice.promises.checkBulkPermissions(v1.CheckBulkPermissionsRequest.create({
		items,
		consistency: { requirement: {
			oneofKind: "fullyConsistent",
			fullyConsistent: true
		} }
	}));
	const result = {};
	for (let i = 0; i < params.permissions.length; i++) {
		const permission = params.permissions[i];
		const pair = response.pairs[i];
		if (pair.response.oneofKind === "item") result[permission] = pair.response.item.permissionship === CheckPermissionResponse_Permissionship.HAS_PERMISSION;
		else result[permission] = false;
	}
	return result;
}
/**
* Find all resources of a type that a subject has a permission on.
*/
async function lookupResources(params) {
	return (await getSpiceClient().promises.lookupResources({
		resourceObjectType: params.resourceType,
		permission: params.permission,
		subject: {
			object: {
				objectType: params.subjectType,
				objectId: params.subjectId
			},
			optionalRelation: ""
		},
		consistency: { requirement: {
			oneofKind: "fullyConsistent",
			fullyConsistent: true
		} },
		context: void 0,
		optionalLimit: 0,
		optionalCursor: void 0
	})).map((item) => item.resourceObjectId);
}
/**
* Write a relationship to SpiceDB.
*/
async function writeRelationship(params) {
	await getSpiceClient().promises.writeRelationships({
		updates: [{
			operation: RelationshipUpdate_Operation.TOUCH,
			relationship: {
				resource: {
					objectType: params.resourceType,
					objectId: params.resourceId
				},
				relation: params.relation,
				subject: {
					object: {
						objectType: params.subjectType,
						objectId: params.subjectId
					},
					optionalRelation: ""
				},
				optionalCaveat: void 0
			}
		}],
		optionalPreconditions: [],
		optionalTransactionMetadata: void 0
	});
}
/**
* Delete a relationship from SpiceDB.
*/
async function deleteRelationship(params) {
	await getSpiceClient().promises.deleteRelationships({
		relationshipFilter: {
			resourceType: params.resourceType,
			optionalResourceId: params.resourceId,
			optionalResourceIdPrefix: "",
			optionalRelation: params.relation,
			optionalSubjectFilter: {
				subjectType: params.subjectType,
				optionalSubjectId: params.subjectId,
				optionalRelation: void 0
			}
		},
		optionalPreconditions: [],
		optionalLimit: 0,
		optionalAllowPartialDeletions: false,
		optionalTransactionMetadata: void 0
	});
}
/**
* Read relationships for a resource to list subjects with access.
* Optionally filter by subject type and ID.
*/
async function readRelationships(params) {
	return (await getSpiceClient().promises.readRelationships({
		relationshipFilter: {
			resourceType: params.resourceType,
			optionalResourceId: params.resourceId || "",
			optionalResourceIdPrefix: "",
			optionalRelation: params.relation || "",
			optionalSubjectFilter: params.subjectType || params.subjectId ? {
				subjectType: params.subjectType || "",
				optionalSubjectId: params.subjectId || "",
				optionalRelation: void 0
			} : void 0
		},
		consistency: { requirement: {
			oneofKind: "fullyConsistent",
			fullyConsistent: true
		} },
		optionalLimit: 0,
		optionalCursor: void 0
	})).map((item) => ({
		resourceId: item.relationship?.resource?.objectId || "",
		subjectType: item.relationship?.subject?.object?.objectType || "",
		subjectId: item.relationship?.subject?.object?.objectId || "",
		relation: item.relationship?.relation || ""
	}));
}

//#endregion
export { Permissionship, RelationshipOperation, checkBulkPermissions, checkPermission, deleteRelationship, getSpiceClient, lookupResources, readRelationships, resetSpiceClient, v1, writeRelationship };