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

function checkCondition(value: any, cond: string, cmp: any): boolean {
  switch (cond) {
    case '!=': return value != cmp;
    case '==': return value == cmp;
    case '>': return value > cmp;
    case '<': return value < cmp;
    case '>=': return value >= cmp;
    case '<=': return value <= cmp;
    default: return false;
  }
}

export class _ForLoop implements INodeType {
  description: INodeTypeDescription = {
    displayName: 'ForLoop',
    name: 'forLoop',
    group: ['transform'],
    version: 1,
    description: 'Цикл for с условием по полю JSON',
    defaults: { name: 'ForLoop' },
    inputs: [NodeConnectionType.Main],
    outputs: [NodeConnectionType.Main],
    properties: [
      {
        displayName: 'Поле (без DATA)',
        name: 'fieldName',
        type: 'string',
        default: 'count',
        placeholder: 'count',
        description: 'Имя поля в item.json.DATA, по которому проверяется условие',
      },
      {
        displayName: 'Условие',
        name: 'condition',
        type: 'options',
        options: [
          { name: '!=', value: '!=' },
          { name: '==', value: '==' },
          { name: '>', value: '>' },
          { name: '<', value: '<' },
          { name: '>=', value: '>=' },
          { name: '<=', value: '<=' },
        ],
        default: '!=',
      },
      {
        displayName: 'Значение для сравнения',
        name: 'compareValue',
        type: 'number',
        default: 0,
      },
      {
        displayName: 'Тип значения',
        name: 'valueType',
        type: 'options',
        options: [
          { name: 'Number', value: 'number' },
          { name: 'String', value: 'string' },
        ],
        default: 'number',
      },
      {
        displayName: 'Начало',
        name: 'start',
        type: 'number',
        default: 1,
      },
      {
        displayName: 'Конец',
        name: 'end',
        type: 'number',
        default: 5,
      },
      {
        displayName: 'Шаг',
        name: 'step',
        type: 'number',
        default: 1,
      },
      {
        displayName: 'Таймаут (мс)',
        name: 'timeoutMs',
        type: 'number',
        default: 0,
      },
    ],
  };

  async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
    const item = this.getInputData()[0]; // всегда один вход
    const returnItems: INodeExecutionData[] = [];

    const fieldName = this.getNodeParameter('fieldName', 0) as string;
    const condition = this.getNodeParameter('condition', 0) as string;
    const compareValue = this.getNodeParameter('compareValue', 0) as number;
    const valueType = this.getNodeParameter('valueType', 0) as string;
    const start = this.getNodeParameter('start', 0) as number;
    const end = this.getNodeParameter('end', 0) as number;
    const step = this.getNodeParameter('step', 0) as number;
    const timeoutMs = this.getNodeParameter('timeoutMs', 0) as number;

    if (step === 0) {
      throw new NodeOperationError(this.getNode(), 'Шаг не может быть равен нулю.');
    }

    const data = item.json.DATA as Record<string, any>;
    if (!data || data[fieldName] === undefined) {
      throw new NodeOperationError(this.getNode(), `В item.json.DATA нет поля "${fieldName}"`);
    }

    let fieldValue = data[fieldName];
    if (valueType === 'number') fieldValue = Number(fieldValue);
    else if (valueType === 'string') fieldValue = String(fieldValue);

    if (!checkCondition(fieldValue, condition, compareValue)) {
      return this.prepareOutputData([]); // условие не прошло — пустой результат
    }

    const loopCondition = (val: number) => step > 0 ? val <= end : val >= end;

    // Получаем данные для метадаты
    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;

    for (let counter = start; loopCondition(counter); counter += step) {
      const newItem: INodeExecutionData = {
        json: {
          ...item.json,
          counter,
          fieldValue,
          METADATA: {
            ...(item.json.METADATA as Record<string, any> || {}),
            chain: workflowName,
            from: fromNode,
            id: idField,
            time: Date.now(),
          },
        },
        binary: item.binary,
      };
      returnItems.push(newItem);

      if (timeoutMs > 0) {
        await new Promise((resolve) => setTimeout(resolve, timeoutMs));
      }
    }

    return this.prepareOutputData(returnItems);
  }
}
