import {
  INodeType,
  INodeTypeDescription,
  ITriggerFunctions,
  ITriggerResponse,
  INodeExecutionData,
  NodeConnectionType,
} from 'n8n-workflow';

import mqtt from 'mqtt';

// Функция замены $var на process.env.VAR
function resolveEnvVariables(str: string): string {
  if (typeof str !== 'string') return str;
  return str.replace(/\$([a-zA-Z0-9_]+)/g, (_, v) => process.env[v] ?? '');
}

export class _MQTTInput implements INodeType {
  description: INodeTypeDescription = {
    displayName: 'MQTTInput',
    name: 'mqttInput',
    group: ['trigger'],
    version: 1,
    description: 'Subscribe to MQTT topics and emit incoming messages',
    defaults: {
      name: 'MQTTInput',
    },
    inputs: [], // триггер — без входа
    outputs: [NodeConnectionType.Main],
    properties: [
      {
        displayName: 'Host URL',
        name: 'host',
        type: 'string',
        default: 'tcp://$mqtthost',
      },
      {
        displayName: 'Port',
        name: 'port',
        type: 'string',
        default: '$mqttport',
      },
      {
        displayName: 'Username',
        name: 'username',
        type: 'string',
        default: '$mqttuser',
      },
      {
        displayName: 'Password',
        name: 'password',
        type: 'string',
       
        default: '$mqttpassword',
      },
      {
        displayName: 'Topic',
        name: 'topic',
        type: 'string',
        default: '$mqtt_topic',
      },
      {
        displayName: 'QoS',
        name: 'qos',
        type: 'number',
        typeOptions: {
          minValue: 0,
          maxValue: 2,
        },
        default: 1,
      },
    ],
  };

  async trigger(this: ITriggerFunctions): Promise<ITriggerResponse> {
    const rawHost = this.getNodeParameter('host', 0) as string;
    const rawPort = this.getNodeParameter('port', 0) as string;
    const rawUsername = this.getNodeParameter('username', 0) as string;
    const rawPassword = this.getNodeParameter('password', 0) as string;
    const rawTopic = this.getNodeParameter('topic', 0) as string;

    const qosRaw = this.getNodeParameter('qos', 0) as number;
    const qos = (qosRaw === 0 || qosRaw === 1 || qosRaw === 2) ? qosRaw : 1;

    // Подставляем переменные окружения
    const host = resolveEnvVariables(rawHost);
    const port = resolveEnvVariables(rawPort);
    const username = resolveEnvVariables(rawUsername);
    const password = resolveEnvVariables(rawPassword);
    const topic = resolveEnvVariables(rawTopic);

    const url = `${host}:${port}`;

    const workflowName = this.getWorkflow()?.name || '';
    const nodeInfo = this.getNode();
    const fromNodeFull = nodeInfo.type;
    const fromNode = fromNodeFull.includes('.') ? fromNodeFull.split('.').pop()! : fromNodeFull;
    const idField = nodeInfo.name as string;

    const client = mqtt.connect(url, {
      username,
      password,
    });

    client.on('connect', () => {
      client.subscribe(topic, { qos }, (err) => {
        if (err) {
          this.emitError(err);
        }
      });
    });

    client.on('message', (receivedTopic, message) => {
      const payloadStr = message.toString();
      let payload: any;
      try {
        payload = JSON.parse(payloadStr);
      } catch {
        payload = { raw: payloadStr };
      }

      const data: INodeExecutionData = {
        json: {
          DATA: payload,
          METADATA: {
            chain: workflowName,
            from: fromNode,
            id: idField,
            time: Date.now(),
            mqtt_topic: receivedTopic,
          },
        },
        pairedItem: 0,
      };

      this.emit([[data]]);
    });

    client.on('error', (err) => {
      this.emitError(err);
    });

    return {
      closeFunction: async () => {
        client.end();
      },
    };
  }
}
