import type { INodeProperties } from 'n8n-workflow';
import { DEFAULT_EVALUATION_METRIC } from 'n8n-workflow';

import {
	CORRECTNESS_PROMPT,
	CORRECTNESS_INPUT_PROMPT,
	HELPFULNESS_PROMPT,
	HELPFULNESS_INPUT_PROMPT,
} from './CannedMetricPrompts.ee';
import { document, sheet } from '../../Google/Sheet/GoogleSheetsTrigger.node';

export const setInputsProperties: INodeProperties[] = [
	{
		displayName:
			'For adding columns from your dataset to the evaluation results. Anything you add here will be displayed in the ‘evaluations’ tab, not the Google Sheet.',
		name: 'setInputsNotice',
		type: 'notice',
		default: '',
		displayOptions: {
			show: {
				operation: ['setInputs'],
			},
		},
	},
	{
		displayName: 'Inputs',
		name: 'inputs',
		placeholder: 'Add Input',
		type: 'fixedCollection',
		typeOptions: {
			multipleValueButtonText: 'Add Input',
			multipleValues: true,
		},
		default: {},
		options: [
			{
				displayName: 'Filter',
				name: 'values',
				values: [
					{
						displayName: 'Name',
						name: 'inputName',
						type: 'string',
						default: '',
						requiresDataPath: 'single',
					},
					{
						displayName: 'Value',
						name: 'inputValue',
						type: 'string',
						default: '',
					},
				],
			},
		],
		displayOptions: {
			show: {
				operation: ['setInputs'],
			},
		},
	},
];

export const setOutputProperties: INodeProperties[] = [
	{
		displayName: 'Credentials',
		name: 'credentials',
		type: 'credentials',
		default: '',
	},
	{
		...document,
		displayName: 'Document Containing Dataset',
		displayOptions: {
			show: {
				operation: ['setOutputs'],
			},
		},
	},
	{
		...sheet,
		displayName: 'Sheet Containing Dataset',
		displayOptions: {
			show: {
				operation: ['setOutputs'],
			},
		},
	},
	{
		displayName: 'Outputs',
		name: 'outputs',
		placeholder: 'Add Output',
		type: 'fixedCollection',
		typeOptions: {
			multipleValueButtonText: 'Add Output',
			multipleValues: true,
		},
		default: {},
		options: [
			{
				displayName: 'Filter',
				name: 'values',
				values: [
					{
						displayName: 'Name',
						name: 'outputName',
						type: 'string',
						default: '',
						requiresDataPath: 'single',
					},
					{
						displayName: 'Value',
						name: 'outputValue',
						type: 'string',
						default: '',
					},
				],
			},
		],
		displayOptions: {
			show: {
				operation: ['setOutputs'],
			},
		},
	},
];

export const setCheckIfEvaluatingProperties: INodeProperties[] = [
	{
		displayName:
			'Routes to the ‘evaluation’ branch if the execution started from an evaluation trigger. Otherwise routes to the ‘normal’ branch.',
		name: 'notice',
		type: 'notice',
		default: '',
		displayOptions: {
			show: {
				operation: ['checkIfEvaluating'],
			},
		},
	},
];

const correctnessFields: INodeProperties[] = [
	{
		displayName: 'Expected Answer',
		name: 'expectedAnswer',
		type: 'string',
		default: '',
		description: 'The expected output defined in your evaluation dataset, used as ground truth',
		displayOptions: {
			show: {
				operation: ['setMetrics'],
				metric: ['correctness', 'stringSimilarity', 'categorization'],
			},
		},
	},
	{
		displayName: 'Actual Answer',
		name: 'actualAnswer',
		type: 'string',
		default: '',
		description: 'The real response generated by AI (e.g. an agent or LLM in the workflow)',
		displayOptions: {
			show: {
				operation: ['setMetrics'],
				metric: ['correctness', 'stringSimilarity', 'categorization'],
			},
		},
	},
];

const helpfulnessFields: INodeProperties[] = [
	{
		displayName: 'User Query',
		name: 'userQuery',
		type: 'string',
		default: '',
		description: 'The original input or question submitted by the user',
		displayOptions: {
			show: {
				operation: ['setMetrics'],
				metric: ['helpfulness'],
			},
		},
	},
	{
		displayName: 'Response',
		name: 'actualAnswer',
		type: 'string',
		default: '',
		description: 'The response generated by AI (e.g. an agent or LLM in the workflow)',
		displayOptions: {
			show: {
				operation: ['setMetrics'],
				metric: ['helpfulness'],
			},
		},
	},
];

function promptFieldForMetric(metric: string, prompt: string): INodeProperties[] {
	return [
		{
			displayName: 'Prompt',
			name: 'prompt',
			type: 'string',
			default: prompt,
			description: `Instruction used to guide the model in scoring the actual answer’s ${metric} against the expected answer`,
			typeOptions: {
				rows: 4,
			},
			displayOptions: {
				show: {
					operation: ['setMetrics'],
					metric: [metric],
				},
			},
		},
	];
}

function optionsForMetric(
	metric: string,
	prompt: string[],
	defaultName: string,
): INodeProperties[] {
	return [
		{
			displayName: 'Options',
			name: 'options',
			type: 'collection',
			default: {},
			placeholder: 'Add Option',
			options: [
				{
					displayName: 'Metric Name',
					name: 'metricName',
					type: 'string',
					default: defaultName,
					description: 'Set this parameter if you want to set a custom name to the metric',
				},
				// eslint-disable-next-line n8n-nodes-base/node-param-default-missing
				{
					displayName: 'Input Prompt',
					name: 'inputPrompt',
					type: 'string',
					default: prompt[0] ?? '',
					typeOptions: {
						rows: 4,
					},
					hint: prompt[1],
				},
			],
			displayOptions: {
				show: {
					operation: ['setMetrics'],
					metric: [metric],
				},
			},
		},
	];
}

function optionsForMetricBasic(metric: string, defaultName: string): INodeProperties[] {
	return [
		{
			displayName: 'Options',
			name: 'options',
			type: 'collection',
			default: {},
			placeholder: 'Add Option',
			options: [
				{
					displayName: 'Metric Name',
					name: 'metricName',
					type: 'string',
					default: defaultName,
				},
			],
			displayOptions: {
				show: {
					operation: ['setMetrics'],
					metric: [metric],
				},
			},
		},
	];
}

const toolsUsedFields: INodeProperties[] = [
	{
		displayName: 'Expected Tools',
		name: 'expectedTools',
		type: 'string',
		default: '',
		description: 'Enter the name(s) of the tool(s) you expect the AI to call (separated by commas)',
		placeholder: 'Get Events, Send Email, Search Database',
		displayOptions: {
			show: {
				operation: ['setMetrics'],
				metric: ['toolsUsed'],
			},
		},
	},
	{
		displayName: 'Intermediate Steps (of Agent)',
		name: 'intermediateSteps',
		type: 'string',
		default: '',
		hint: 'The output field of the agent containing the tools called. To see it, enable returning intermediate steps in the agent’s options',
		displayOptions: {
			show: {
				operation: ['setMetrics'],
				metric: ['toolsUsed'],
			},
		},
	},
];

export const setMetricsProperties: INodeProperties[] = [
	{
		displayName:
			'Metrics measure the quality of an execution. They will be displayed in the ‘evaluations’ tab, not the Google Sheet.',
		//			"Calculate the score(s) for the evaluation, then map them into this node. They will be displayed in the ‘evaluations’ tab, not the Google Sheet. <a href='https://docs.n8n.io/advanced-ai/evaluations/metric-based-evaluations/#2-calculate-metrics' target='_blank'>View metric examples</a>",
		name: 'notice',
		type: 'notice',
		default: '',
		displayOptions: {
			show: {
				operation: ['setMetrics'],
			},
		},
	},
	{
		displayName: 'Metric',
		name: 'metric',
		type: 'hidden',
		default: 'customMetrics',
		displayOptions: {
			show: {
				operation: ['setMetrics'],
				'@version': [4.6],
			},
		},
	},
	{
		displayName: 'Metric',
		name: 'metric',
		type: 'options',
		noDataExpression: true,
		// eslint-disable-next-line n8n-nodes-base/node-param-options-type-unsorted-items
		options: [
			{
				// eslint-disable-next-line n8n-nodes-base/node-param-display-name-miscased
				name: 'Correctness (AI-based)',
				value: 'correctness',
				description:
					'Whether the answer’s meaning is consistent with a reference answer. Uses a scale of 1 (worst) to 5 (best).',
			},
			{
				// eslint-disable-next-line n8n-nodes-base/node-param-display-name-miscased
				name: 'Helpfulness (AI-based)',
				value: 'helpfulness',
				description:
					'Whether the response addresses the query. Uses a scale of 1 (worst) to 5 (best).',
			},
			{
				name: 'String Similarity',
				value: 'stringSimilarity',
				description:
					'How close the answer is to a reference answer, measured character-by-character (edit distance). Returns a score between 0 and 1.',
			},
			{
				name: 'Categorization',
				value: 'categorization',
				description:
					'Whether the answer exactly matches the reference answer. Returns 1 if so and 0 otherwise.',
			},
			{
				name: 'Tools Used',
				value: 'toolsUsed',
				description: 'Whether tool(s) were used or not. Returns a score between 0 and 1.',
			},
			{
				name: 'Custom Metrics',
				value: 'customMetrics',
				description: 'Define your own metric(s)',
			},
		],
		default: DEFAULT_EVALUATION_METRIC,
		displayOptions: {
			show: {
				operation: ['setMetrics'],
				'@version': [{ _cnd: { gte: 4.7 } }],
			},
		},
	},
	...correctnessFields,
	...helpfulnessFields,
	...toolsUsedFields,
	...promptFieldForMetric('correctness', CORRECTNESS_PROMPT),
	...promptFieldForMetric('helpfulness', HELPFULNESS_PROMPT),
	{
		displayName:
			"Calculate the custom metrics before this node, then map them below. <a href='https://docs.n8n.io/advanced-ai/evaluations/metric-based-evaluations/#2-calculate-metrics' target='_blank'>View metric examples</a>",
		name: 'notice',
		type: 'notice',
		default: '',
		displayOptions: {
			show: {
				operation: ['setMetrics'],
				metric: ['customMetrics'],
			},
		},
	},
	{
		displayName: 'Metrics to Return',
		name: 'metrics',
		type: 'assignmentCollection',
		default: {
			assignments: [
				{
					name: '',
					value: '',
					type: 'number',
				},
			],
		},
		typeOptions: {
			assignment: {
				disableType: true,
				defaultType: 'number',
			},
		},
		displayOptions: {
			show: {
				operation: ['setMetrics'],
				metric: ['customMetrics'],
			},
		},
	},
	...optionsForMetric('correctness', CORRECTNESS_INPUT_PROMPT, 'Correctness'),
	...optionsForMetric('helpfulness', HELPFULNESS_INPUT_PROMPT, 'Helpfulness'),
	...optionsForMetricBasic('categorization', 'Categorization'),
	...optionsForMetricBasic('stringSimilarity', 'String similarity'),
	...optionsForMetricBasic('toolsUsed', 'Tools Used'),
];
