/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

// TLS integration tests for the Node.js SDK.
//
// These tests require a TLS-enabled Iggy server and are skipped by default.
// To run them locally:
//
//   1. Start the server with TLS:
//        IGGY_ROOT_USERNAME=iggy IGGY_ROOT_PASSWORD=iggy \
//        IGGY_TCP_TLS_ENABLED=true \
//        IGGY_TCP_TLS_CERT_FILE=core/certs/iggy_cert.pem \
//        IGGY_TCP_TLS_KEY_FILE=core/certs/iggy_key.pem \
//        cargo r --bin iggy-server
//
//   2. Run the tests:
//        cd foreign/node
//        IGGY_TCP_TLS_ENABLED=true IGGY_TCP_ADDRESS=127.0.0.1:8090 \
//        node --import @swc-node/register/esm-register --test src/e2e/tls.system.e2e.ts

import { readFileSync } from 'node:fs';
import { resolve } from 'node:path';
import { after, describe, it } from 'node:test';
import assert from 'node:assert/strict';
import { Client } from '../client/client.js';
import { Partitioning, Consumer, PollingStrategy } from '../wire/index.js';
import { getIggyAddress } from '../tcp.sm.utils.js';

const tlsEnabled = process.env.IGGY_TCP_TLS_ENABLED === 'true';

// Path to the CA certificate. Override with E2E_ROOT_CA_CERT env var,
// otherwise fall back to the default relative path from the repo root.
const caCertPath = process.env.E2E_ROOT_CA_CERT
  || resolve(process.cwd(), '../../core/certs/iggy_ca_cert.pem');

const getTlsClient = () => {
  const [, port] = getIggyAddress();
  const caCert = readFileSync(caCertPath);

  // The server certificate SAN is DNS:localhost, so we connect via 'localhost'
  // for proper hostname verification (consistent with Python and C# TLS tests).
  return new Client({
    transport: 'TLS',
    options: {
      port,
      host: 'localhost',
      ca: caCert,
    },
    credentials: { username: 'iggy', password: 'iggy' },
  });
};

describe('e2e -> tls', { skip: !tlsEnabled && 'IGGY_TCP_TLS_ENABLED is not set' }, async () => {

  const c = getTlsClient();
  const credentials = { username: 'iggy', password: 'iggy' };

  it('e2e -> tls::ping', async () => {
    assert.ok(await c.system.ping());
  });

  it('e2e -> tls::login', async () => {
    assert.deepEqual(
      await c.session.login(credentials),
      { userId: 0 }
    );
  });

  it('e2e -> tls::getStats', async () => {
    const stats = await c.system.getStats();
    assert.ok(stats);
    assert.ok('processId' in stats);
    assert.ok('hostname' in stats);
  });

  it('e2e -> tls::send and poll messages', async () => {
    const streamName = 'tls-e2e-stream';
    const topicName = 'tls-e2e-topic';

    await c.stream.create({ name: streamName });
    await c.topic.create({
      streamId: streamName,
      name: topicName,
      partitionCount: 1,
      compressionAlgorithm: 1,
    });

    const messages = [
      { id: 1, headers: [], payload: 'tls-message-1' },
      { id: 2, headers: [], payload: 'tls-message-2' },
      { id: 3, headers: [], payload: 'tls-message-3' },
    ];

    await c.message.send({
      streamId: streamName,
      topicId: topicName,
      messages,
      partition: Partitioning.PartitionId(0),
    });

    const polled = await c.message.poll({
      streamId: streamName,
      topicId: topicName,
      consumer: Consumer.Single,
      partitionId: 0,
      pollingStrategy: PollingStrategy.First,
      count: 10,
      autocommit: false,
    });

    assert.equal(polled.messages.length, 3);

    await c.stream.delete({ streamId: streamName });
  });

  it('e2e -> tls::logout', async () => {
    assert.ok(await c.session.logout());
  });

  after(() => {
    c.destroy();
  });
});
