import { DynamicPropsValue, Property, createAction } from '@activepieces/pieces-framework';
import { JiraDataCenterAuth, jiraDataCenterAuth } from '../../auth';
import {
	createPropertyDefinition,
	formatIssueFields,
	issueIdOrKeyProp,
	issueStatusIdProp,
	transformCustomFields,
} from '../common/props';
import { jiraApiCall } from '../common';
import { IssueFieldMetaData, VALID_CUSTOM_FIELD_TYPES } from '../common/types';
import { HttpMethod } from '@activepieces/pieces-common';
import { isNil } from '@activepieces/pieces-framework';

async function getFields(auth: JiraDataCenterAuth, issueId: string): Promise<IssueFieldMetaData[]> {
	const response = await jiraApiCall<{ fields: { [x: string]: Omit<IssueFieldMetaData, 'key' | 'fieldId'> } }>({
		auth: auth,
		method: HttpMethod.GET,
		resourceUri: `/issue/${issueId}/editmeta`,
	});

	if (!response.fields) return [];

	return Object.entries(response.fields).map(([key, field]) => ({
		...field,
		key,
		fieldId: key,
	}));
}

export const updateIssueAction = createAction({
	name: 'update_issue',
	displayName: 'Update Issue',
	description: 'Updates an existing issue.',
	audience: 'both',
	aiMetadata: {
		description:
			'Updates fields on an existing Jira Data Center/Server issue, identified by its ID or key. Can optionally transition the issue to a new status (workflow transition) in the same call when a status is supplied. Use to edit an already-created issue rather than create one. Repeating with the same field values converges on the same state, so it is effectively idempotent.',
		idempotent: true,
	},
	auth: jiraDataCenterAuth,
	props: {
		issueId: issueIdOrKeyProp('Issue ID or Key', true),
		statusId: issueStatusIdProp('Status', false),
		issueFields: Property.DynamicProperties({
			auth: jiraDataCenterAuth,
			displayName: 'Fields',
			required: true,
			refreshers: ['issueId'],
			props: async ({ auth, issueId }) => {
				if (!auth || !issueId) {
					return {};
				}

				const props: DynamicPropsValue = {};

				const authValue = auth as JiraDataCenterAuth;
				const issueIdValue = issueId as unknown as string;

				const fields = await getFields(authValue, issueIdValue);

				for (const field of fields) {
					// skip invalid custom fields
					if (field.schema.custom) {
						const customFieldType = field.schema.custom.split(':')[1];
						if (!VALID_CUSTOM_FIELD_TYPES.includes(customFieldType)) {
							continue;
						}
					}

					if (field.key === 'issuetype') {
						props[field.key] = Property.StaticDropdown({
							displayName: field.name,
							required: false,
							options: {
								disabled: false,
								options: field.allowedValues
									? field.allowedValues.map((option) => ({
											label: option.name,
											value: option.id,
									  }))
									: [],
							},
						});
					} else {
						props[field.key] = await createPropertyDefinition(authValue, field, false);
					}
				}
				// Remove null props
				return Object.fromEntries(Object.entries(props).filter(([_, prop]) => prop !== null));
			},
		}),
	},
	async run(context) {
		const { issueId, statusId } = context.propsValue;
		const inputIssueFields = context.propsValue.issueFields ?? {};

		if (isNil(issueId)) {
			throw new Error('Issue ID is required');
		}

		if (!isNil(statusId) && statusId !== '') {
			await jiraApiCall({
				auth: context.auth,
				method: HttpMethod.POST,
				resourceUri: `/issue/${issueId}/transitions`,
				body: {
					transition: {
						id: statusId,
					},
				},
			});
		}

		const issueTypeFieldsResponse = await jiraApiCall<{ fields: { [x: string]: Omit<IssueFieldMetaData, 'key' | 'fieldId'> } }>({
			auth: context.auth,
			method: HttpMethod.GET,
			resourceUri: `/issue/${issueId}/editmeta`,
		});

		const flattenedFields = Object.entries(issueTypeFieldsResponse.fields ?? {}).map(([key, field]) => ({
			...field,
			key,
			fieldId: key,
		}));

		const formattedFields = formatIssueFields(flattenedFields, inputIssueFields);

		await jiraApiCall({
			auth: context.auth,
			method: HttpMethod.PUT,
			resourceUri: `/issue/${issueId}`,
			body: {
				fields: formattedFields,
			},
		});

		const issue = await jiraApiCall<{
			expand: string;
			id: string;
			key: string;
			fields: Record<string, unknown>;
		}>({
			auth: context.auth,
			method: HttpMethod.GET,
			resourceUri: `/issue/${issueId}`,
		});

		const updatedIssueProperties = transformCustomFields(flattenedFields, issue.fields);

		return {
			...issue,
			fields: updatedIssueProperties,
		};
	},
});
