import {
  INodeType,
  INodeTypeDescription,
  IExecuteFunctions,
  INodeExecutionData,
  NodeOperationError,
  NodeConnectionType,
  IDataObject,
} from 'n8n-workflow';

function isObject(obj: unknown): obj is Record<string, any> {
  return typeof obj === 'object' && obj !== null && !Array.isArray(obj);
}

export class _ChainOut implements INodeType {
  description: INodeTypeDescription = {
    displayName: 'ChainOut',
    name: 'chainOut',
    group: ['transform'],
    version: 1,
    description: 'Отправляет данные в указанный workflow по ID',
    defaults: {
      name: 'ChainOut',
    },
    inputs: [NodeConnectionType.Main],
    outputs: [NodeConnectionType.Main],
    properties: [
      {
        displayName: 'Workflow ID Source',
        name: 'workflowIdSource',
        type: 'options',
        options: [
          {
            name: 'Параметр',
            value: 'parameter',
            description: 'Брать ID цепочки из параметра',
          },
          {
            name: 'METADATA.ChainOut.name',
            value: 'metadata',
            description: 'Брать ID цепочки из метаданных',
          },
        ],
        default: 'parameter',
        description: 'Откуда брать ID цепочки назначения',
      },
      {
        displayName: 'Workflow ID',
        name: 'workflowId',
        type: 'string',
        default: '',
        required: true,
        displayOptions: {
          show: {
            workflowIdSource: ['parameter'],
          },
        },
        description: 'ID workflow для запуска',
      },
    ],
  };

  async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
    const items = this.getInputData();
    if (!items || items.length === 0) return [[]];

    const item = items[0];
    let workflowId = '';
    const workflowIdSource = this.getNodeParameter('workflowIdSource', 0) as string;

    if (workflowIdSource === 'parameter') {
      workflowId = this.getNodeParameter('workflowId', 0) as string;
    } else {
      const metadataRaw = item.json.METADATA;
      if (!isObject(metadataRaw)) {
        throw new NodeOperationError(this.getNode(), 'METADATA не является объектом');
      }
      const metadata = metadataRaw as Record<string, any>;

      if (!isObject(metadata.ChainOut)) {
        throw new NodeOperationError(this.getNode(), 'METADATA.ChainOut не является объектом');
      }
      const chainOut = metadata.ChainOut as Record<string, any>;
      const name = chainOut.name;
      if (typeof name !== 'string' || name.trim() === '') {
        throw new NodeOperationError(this.getNode(), 'Workflow ID не найден в METADATA.ChainOut.name');
      }
      workflowId = name;
    }

    if (!workflowId) {
      throw new NodeOperationError(this.getNode(), 'Workflow ID пустой');
    }

    // Формируем новую METADATA
    const nodeInfo = this.getNode();
    const fromNodeFull = nodeInfo.type;
    const fromNode = fromNodeFull.includes('.') ? fromNodeFull.split('.').pop()! : fromNodeFull;
    const idField = nodeInfo.name;
    const workflowName = this.getWorkflow().name;

    item.json.METADATA = {
      chain: workflowName,
      from: fromNode,
      id: idField,
      time: Date.now(),
    } as IDataObject;

    try {
      const executionResult = await this.executeWorkflow(
        { id: workflowId },
        [item], // передаём массив с одним элементом
        undefined,
        {
          parentExecution: {
            executionId: this.getWorkflowDataProxy(0).$execution.id,
            workflowId: this.getWorkflowDataProxy(0).$workflow.id,
          },
        },
      );

      return executionResult.data as INodeExecutionData[][];
    } catch (error: any) {
      throw new NodeOperationError(this.getNode(), `Ошибка запуска workflow: ${error.message}`);
    }
  }
}
