import type { Role } from '@n8n/api-types';
import { hasScope as genericHasScope } from '@n8n/permissions';
import type { ScopeOptions, Scope, Resource } from '@n8n/permissions';
import { defineStore } from 'pinia';
import { ref } from 'vue';

import { STORES } from './constants';

export const useRBACStore = defineStore(STORES.RBAC, () => {
	const globalRoles = ref<Role[]>([]);
	const rolesByProjectId = ref<Record<string, string[]>>({});

	const globalScopes = ref<Scope[]>([]);
	const scopesByProjectId = ref<Record<string, Scope[]>>({});
	const scopesByResourceId = ref<Record<Resource, Record<string, Scope[]>>>({
		agent: {},
		aiAssistant: {},
		workflow: {},
		tag: {},
		annotationTag: {},
		user: {},
		credential: {},
		variable: {},
		projectVariable: {},
		sourceControl: {},
		gitConnection: {},
		externalSecretsProvider: {},
		externalSecret: {},
		project: {},
		orchestration: {},
		workersView: {},
		eventBusDestination: {},
		auditLogs: {},
		banner: {},
		community: {},
		communityPackage: {},
		ldap: {},
		license: {},
		logStreaming: {},
		nodeTypePolicy: {},
		saml: {},
		oidc: {},
		provisioning: {},
		securityAudit: {},
		folder: {},
		insights: {},
		dataTable: {},
		execution: {},
		testRun: {},
		workflowTags: {},
		role: {},
		mcp: {},
		mcpApiKey: {},
		chatHub: {},
		chatHubAgent: {},
		breakingChanges: {},
		apiKey: {},
		encryptionKey: {},
		credentialResolver: {},
		instanceAi: {},
		securitySettings: {},
		roleMappingRule: {},
		otel: {},
	});

	function addGlobalRole(role: Role) {
		if (!globalRoles.value.includes(role)) {
			globalRoles.value.push(role);
		}
	}

	function hasRole(role: Role) {
		return globalRoles.value.includes(role);
	}

	function addGlobalScope(scope: Scope) {
		if (!globalScopes.value.includes(scope)) {
			globalScopes.value.push(scope);
		}
	}

	function setGlobalScopes(scopes: Scope[]) {
		globalScopes.value = scopes;
	}

	function addProjectScope(
		scope: Scope,
		context: {
			projectId: string;
		},
	) {
		if (!scopesByProjectId.value[context.projectId]) {
			scopesByProjectId.value[context.projectId] = [];
		}

		if (!scopesByProjectId.value[context.projectId].includes(scope)) {
			scopesByProjectId.value[context.projectId].push(scope);
		}
	}

	function addResourceScope(
		scope: Scope,
		context: {
			resourceType: Resource;
			resourceId: string;
		},
	) {
		const scopesByResourceType = scopesByResourceId.value[context.resourceType];
		if (!scopesByResourceType[context.resourceId]) {
			scopesByResourceType[context.resourceId] = [];
		}

		if (!scopesByResourceType[context.resourceId].includes(scope)) {
			scopesByResourceType[context.resourceId].push(scope);
		}
	}

	function hasScope(
		scope: Scope | Scope[],
		context?: {
			resourceType?: Resource;
			resourceId?: string;
			projectId?: string;
		},
		options?: ScopeOptions,
	): boolean {
		return genericHasScope(
			scope,
			{
				global: globalScopes.value,
				project: context?.projectId ? scopesByProjectId.value[context.projectId] : [],
				resource:
					context?.resourceType && context?.resourceId
						? scopesByResourceId.value[context.resourceType][context.resourceId]
						: [],
			},
			undefined,
			options,
		);
	}

	return {
		globalRoles,
		rolesByProjectId,
		globalScopes,
		scopesByProjectId,
		scopesByResourceId,
		addGlobalRole,
		hasRole,
		addGlobalScope,
		setGlobalScopes,
		addProjectScope,
		addResourceScope,
		hasScope,
	};
});
