import type {
	INodeTypeDescription,
	NodeConnectionType,
	TriggerPanelDefinition,
} from 'n8n-workflow';
import { nodeConnectionTypes } from 'n8n-workflow';
import type { ProjectRole, TeamProjectRole } from '@n8n/permissions';
import type {
	NewCredentialsModal,
	CredentialsResource,
	FolderResource,
	Resource,
	VariableResource,
	WorkflowResource,
	WorkflowListItem,
	WorkflowListResource,
} from '@/Interface';
import type { IExecutionResponse } from '@/features/execution/executions/executions.types';
import type { ICredentialsResponse } from '@/features/credentials/credentials.types';
import type { Connection as VueFlowConnection } from '@vue-flow/core';
import type { RouteLocationRaw } from 'vue-router';
import type { CanvasConnectionMode } from '@/features/workflows/canvas/canvas.types';
import { canvasConnectionModes } from '@/features/workflows/canvas/canvas.types';
import type { ComponentPublicInstance } from 'vue';
import { type BaseTextKey, useI18n } from '@n8n/i18n';

/*
	Type guards used in editor-ui project
*/

export const checkExhaustive = (value: never): never => {
	throw new Error(`Unhandled value: ${value}`);
};

export function isNotNull<T>(value: T | null): value is T {
	return value !== null;
}

export function isValidCredentialResponse(value: unknown): value is ICredentialsResponse {
	return typeof value === 'object' && value !== null && 'id' in value;
}

export const isObj = (obj: unknown): obj is object =>
	!!obj && Object.getPrototypeOf(obj) === Object.prototype;

export const isBinaryLike = (obj: unknown): obj is object =>
	isObj(obj) && 'mimeType' in obj && 'data' in obj;

export function isString(value: unknown): value is string {
	return typeof value === 'string';
}

export function isNumber(value: unknown): value is number {
	return typeof value === 'number';
}

export const isCredentialModalState = (value: unknown): value is NewCredentialsModal => {
	return typeof value === 'object' && value !== null && 'showAuthSelector' in value;
};

export const isResourceMapperValue = (value: unknown): value is string | number | boolean => {
	return ['string', 'number', 'boolean'].includes(typeof value);
};

export function isDateObject(date: unknown): date is Date {
	return (
		!!date && Object.prototype.toString.call(date) === '[object Date]' && !isNaN(date as number)
	);
}

export function isValidNodeConnectionType(
	connectionType: string | undefined,
): connectionType is NodeConnectionType {
	return nodeConnectionTypes.includes(connectionType as NodeConnectionType);
}

export function isValidCanvasConnectionMode(mode: string): mode is CanvasConnectionMode {
	return canvasConnectionModes.includes(mode as CanvasConnectionMode);
}

export function isVueFlowConnection(connection: object): connection is VueFlowConnection {
	return (
		'source' in connection &&
		'target' in connection &&
		'sourceHandle' in connection &&
		'targetHandle' in connection
	);
}

export function isTriggerPanelObject(
	triggerPanel: INodeTypeDescription['triggerPanel'],
): triggerPanel is TriggerPanelDefinition {
	return triggerPanel !== undefined && typeof triggerPanel === 'object' && triggerPanel !== null;
}

export function isFullExecutionResponse(
	execution: IExecutionResponse | null,
): execution is IExecutionResponse {
	return !!execution && 'status' in execution;
}

export function isRouteLocationRaw(value: unknown): value is RouteLocationRaw {
	return (
		typeof value === 'string' ||
		(typeof value === 'object' && value !== null && ('name' in value || 'path' in value))
	);
}

export function isComponentPublicInstance(value: unknown): value is ComponentPublicInstance {
	return value !== null && typeof value === 'object' && '$props' in value;
}

export function isWorkflowResource(value: Resource): value is WorkflowResource {
	return value.resourceType === 'workflow';
}

export function isFolderResource(value: Resource): value is FolderResource {
	return value.resourceType === 'folder';
}

export function isVariableResource(value: Resource): value is VariableResource {
	return value.resourceType === 'variable';
}

export function isCredentialsResource(value: Resource): value is CredentialsResource {
	return value.resourceType === 'credential';
}

export function isSharedResource(
	value: Resource,
): value is WorkflowResource | FolderResource | CredentialsResource {
	return isWorkflowResource(value) || isFolderResource(value) || isCredentialsResource(value);
}

export function isResourceSortableByDate(
	value: Resource,
): value is WorkflowResource | FolderResource | CredentialsResource {
	return isWorkflowResource(value) || isFolderResource(value) || isCredentialsResource(value);
}

// Check if i18n key is a valid BaseTextKey
export function isBaseTextKey(key: string): key is BaseTextKey {
	const i18n = useI18n();
	try {
		// Attempt to access the base text to check if the key is valid
		i18n.baseText(key as BaseTextKey);
		return true;
	} catch {
		// If an error is thrown, the key is not valid
		return false;
	}
}

// Type guard to check if a string is a valid ProjectRole
export function isProjectRole(role: string): role is ProjectRole {
	return role.startsWith('project:');
}

// Type guard to check if a role is a valid TeamProjectRole (ProjectRole excluding personalOwner)
export function isTeamProjectRole(role: string): role is TeamProjectRole {
	return isProjectRole(role) && role !== 'project:personalOwner';
}

export const isWorkflowListItem = (resource: WorkflowListResource): resource is WorkflowListItem =>
	'resource' in resource ? resource.resource !== 'folder' : true;

export function isDebouncedFunction(fn: unknown): fn is { cancel: () => void } {
	return (
		typeof fn === 'function' &&
		'cancel' in fn &&
		typeof (fn as Record<string, unknown>).cancel === 'function'
	);
}
