import type {
  IExecuteFunctions,
  ILoadOptionsFunctions,
  INodeExecutionData,
  INodeType,
  INodeTypeDescription,
} from 'n8n-workflow';
import { NodeConnectionTypes, NodeOperationError } from 'n8n-workflow';

import {
  DATA_PRODUCT_BASE_PATH,
  DATA_PLANE_DEFAULT_BASE_URL,
  DATA_PLANE_BASE_URL_ENV,
  DATA_PRODUCT_WRITER_CATALOG_PATH,
  NDC_DATA_PRODUCT_WRITER_CREDENTIAL,
  NDC_NODE_ICON,
} from '../shared/constants';
import { buildPublishPayload, encodeIdentifierPath } from '../shared/contracts';
import { safeHttpError, safeInputError } from '../shared/errors';
import { loadDataProductOptions, ndcRequest, serviceBaseUrl } from '../shared/http';

export class NdcDataProductPublish implements INodeType {
  description: INodeTypeDescription = {
    displayName: 'NDC Data Product Publish',
    name: 'ndcDataProductPublish',
    icon: NDC_NODE_ICON,
    group: ['output'],
    version: [1, 2],
    subtitle: '={{$parameter["dataProductId"]}}',
    description: 'Publish canonical facts through a scoped NDC Data Product grant',
    defaults: { name: 'NDC Data Product Publish' },
    inputs: [NodeConnectionTypes.Main],
    outputs: [NodeConnectionTypes.Main],
    credentials: [{ name: NDC_DATA_PRODUCT_WRITER_CREDENTIAL, required: true }],
    properties: [
      {
        displayName: 'Data Product Name or ID',
        name: 'dataProductId',
        type: 'options',
        typeOptions: { loadOptionsMethod: 'getDataProducts' },
        default: '',
        required: true,
      },
      {
        displayName: 'Publish Mode',
        name: 'publishMode',
        type: 'options',
        options: [
          { name: 'Create or Update', value: 'upsert' },
          { name: 'Replace Complete Snapshot', value: 'replace' },
        ],
        default: 'upsert',
        required: true,
        displayOptions: { show: { '@version': [2] } },
        description: 'Replace commits one complete generation atomically and removes keys absent from it',
      },
      {
        displayName: 'Batch Sequence',
        name: 'sequence',
        type: 'number',
        typeOptions: { minValue: 0, numberStepSize: 1 },
        default: 0,
        required: true,
        description: 'Sequence number when one execution publishes several batches',
      },
    ],
  };

  methods = {
    loadOptions: {
      async getDataProducts(this: ILoadOptionsFunctions) {
        return loadDataProductOptions(
          this,
          NDC_DATA_PRODUCT_WRITER_CREDENTIAL,
          DATA_PLANE_BASE_URL_ENV,
          DATA_PLANE_DEFAULT_BASE_URL,
          DATA_PRODUCT_WRITER_CATALOG_PATH,
        );
      },
    },
  };

  async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
    const inputItems = this.getInputData();
    const dataProductId = this.getNodeParameter('dataProductId', 0) as string;
    const sequence = this.getNodeParameter('sequence', 0, 0) as number;
    const publishMode = this.getNode().typeVersion >= 2
      ? this.getNodeParameter('publishMode', 0, 'upsert') as 'upsert' | 'replace'
      : 'upsert';
    let body;
    let encodedProductId;
    try {
      body = buildPublishPayload(
        inputItems,
        this.getExecutionId(),
        String(this.getWorkflow().id ?? 'workflow'),
        this.getNode().id,
        dataProductId,
        sequence,
        publishMode,
      );
      encodedProductId = encodeIdentifierPath(dataProductId, 'dataProductId');
    } catch (error) {
      throw new NodeOperationError(this.getNode(), safeInputError(error, 'ndc_data_product_publish_input_invalid'));
    }

    try {
      const baseUrl = serviceBaseUrl(DATA_PLANE_BASE_URL_ENV, DATA_PLANE_DEFAULT_BASE_URL);
      const response = await ndcRequest(this, NDC_DATA_PRODUCT_WRITER_CREDENTIAL, {
        method: 'POST',
        url: `${baseUrl}${DATA_PRODUCT_BASE_PATH}/${encodedProductId}/publish`,
        body,
      });
      return [[{
        json: response as INodeExecutionData['json'],
        pairedItem: inputItems.map((_item, index) => ({ item: index })),
      }]];
    } catch (error) {
      throw new NodeOperationError(this.getNode(), safeHttpError(error, 'ndc_data_product_publish_failed'));
    }
  }
}
