/**
 * Copyright (C) 2026 Huawei Device Co., Ltd.
 *
 * This software is distributed under a license. The full license
 * agreement can be found in the file LICENSE in this distribution.
 * This software may not be copied, modified, sold or distributed
 * other than expressed in the named license agreement.
 *
 * This software is distributed without any warranty.
 */
/**
 * @ohos/socketio Demo 测试服务端
 *
 * 端口:6600
 * 支持的 Demo 列表:
 *   001 - Socket连接建立与状态查询   → 建立连接即可触发 open_listener
 *   002 - 连接全生命周期监听         → 建立/断开连接触发各生命周期回调
 *   003 - Socket命名空间配置与监听   → /test 命名空间 echo
 *   004 - 事件发送与接收             → chat 事件 echo
 *   005 - 多参数事件与一次性监听     → multiEvent echo、onceEvent echo
 *   006 - 二进制事件收发             → binaryEvent echo(Buffer)
 *   007 - ACK确认回调发送            → ackEvent ACK 回调、binaryAckEvent 二进制 ACK
 *   008 - 错误事件监听与取消         → 连接无效地址由客户端触发,服务端无需特殊处理
 *   009 - HTTP请求头与连接选项配置   → 服务端打印 handshake headers 验证
 *   010 - 重连策略参数配置           → 服务端可主动断开连接触发重连
 *   011 - 连接关闭与监听器清理       → 建立/断开连接即可
 *   012 - 代理服务器认证配置         → 需通过代理连接,服务端正常监听即可
 *
 * 启动方式:
 *   npm install
 *   npm start
 *
 * 或直接:node index.js
 */

'use strict';

const { Server } = require('socket.io');
const http = require('http');

const PORT = 6600;

// ─── HTTP 服务器(socket.io v4 需要挂载在 http.Server 上)─────────────────
const httpServer = http.createServer((req, res) => {
    res.writeHead(200, { 'Content-Type': 'text/plain' });
    res.end('@ohos/socketio demo server is running on port ' + PORT + '\n');
});

const io = new Server(httpServer, {
    // 允许所有来源跨域(Demo 测试环境)
    cors: {
        origin: '*',
        methods: ['GET', 'POST']
    },
    // 允许 socket.io v2/v3/v4 客户端连接
    allowEIO3: true
});

console.log('='.repeat(60));
console.log('  @ohos/socketio Demo 测试服务端');
console.log('  端口: ' + PORT);
console.log('='.repeat(60));

// ═══════════════════════════════════════════════════════════════
//  默认命名空间 /  ── 支持 Demo 001~012(除 003 独立命名空间)
// ═══════════════════════════════════════════════════════════════
io.on('connection', (socket) => {
    console.log('\n[/] 新连接  socket.id=' + socket.id);

    // ── Demo 009:打印握手请求头,验证 set_headers() 配置已生效 ──────────
    const headers = socket.handshake.headers;
    if (headers['authorization']) {
        console.log('[/] [Demo009] Authorization header: ' + headers['authorization']);
    }
    if (headers['x-custom-header']) {
        console.log('[/] [Demo009] custom header: ' + headers['x-custom-header']);
    }

    // ── Demo 001/002/011/012:连接成功,触发客户端 open_listener ─────────
    // (连接本身即可触发,无需额外逻辑)

    // ─────────────────────────────────────────────────────────────────────
    //  Demo 004:chat 事件 echo
    //  客户端 emit('chat', msg) → 服务端 echo 回 chat 事件
    // ─────────────────────────────────────────────────────────────────────
    socket.on('chat', (...args) => {
        console.log('[/] [Demo004] chat received:', args);
        // echo 回同一个事件,触发客户端 on('chat', callback)
        socket.emit('chat', ...args);
    });

    // ─────────────────────────────────────────────────────────────────────
    //  Demo 005:multiEvent echo(多参数)
    //  客户端 emit('multiEvent', 'param1', 42, true) → 服务端 echo 多参数
    // ─────────────────────────────────────────────────────────────────────
    socket.on('multiEvent', (...args) => {
        console.log('[/] [Demo005] multiEvent received, args count=' + args.length, args);
        // 多参数 echo:逐个参数回传,客户端 on_multi 回调会收到展开的 args
        socket.emit('multiEvent', ...args);
    });

    // ─────────────────────────────────────────────────────────────────────
    //  Demo 005:onceEvent echo(once 单次验证)
    //  客户端 once('onceEvent', callback),首次 echo 触发,后续不再触发
    // ─────────────────────────────────────────────────────────────────────
    socket.on('onceEvent', (...args) => {
        console.log('[/] [Demo005] onceEvent received:', args);
        socket.emit('onceEvent', ...args);
    });

    // ─────────────────────────────────────────────────────────────────────
    //  Demo 006:binaryEvent echo(二进制 Buffer)
    //  客户端 emit('binaryEvent', ArrayBuffer) → 服务端 echo Buffer
    // ─────────────────────────────────────────────────────────────────────
    socket.on('binaryEvent', (...args) => {
        const data = args[0];
        if (Buffer.isBuffer(data)) {
            console.log('[/] [Demo006] binaryEvent received, Buffer length=' + data.length);
        } else if (data instanceof Uint8Array || ArrayBuffer.isView(data)) {
            console.log('[/] [Demo006] binaryEvent received, TypedArray length=' + data.byteLength);
        } else {
            console.log('[/] [Demo006] binaryEvent received (non-binary):', data);
        }
        // echo 回 Buffer,客户端 on_binary 回调接收到 Uint8Array
        socket.emit('binaryEvent', data);
    });

    // ─────────────────────────────────────────────────────────────────────
    //  Demo 007:ackEvent —— emit + ACK 回调
    //  客户端 emit('ackEvent', payload, callback) → 服务端调用 callback
    // ─────────────────────────────────────────────────────────────────────
    socket.on('ackEvent', (...args) => {
        console.log('[/] [Demo007] ackEvent received:', args);
        // 最后一个参数是 ACK 回调函数
        const ackFn = args[args.length - 1];
        if (typeof ackFn === 'function') {
            const response = {
                status: 'ok',
                echo: args.slice(0, -1),
                timestamp: Date.now()
            };
            console.log('[/] [Demo007] calling ACK callback with:', response);
            ackFn(response);
        }
    });

    // ─────────────────────────────────────────────────────────────────────
    //  Demo 007:binaryAckEvent —— emitAckBinary 二进制 ACK
    //  客户端 emitAckBinary('binaryAckEvent', message, callback(code, Uint8Array))
    //  服务端回传 [code, Buffer]
    // ─────────────────────────────────────────────────────────────────────
    socket.on('binaryAckEvent', (...args) => {
        console.log('[/] [Demo007] binaryAckEvent received:', args);
        const ackFn = args[args.length - 1];
        if (typeof ackFn === 'function') {
            // 回传 code=200 + Buffer("ACK_OK" 的字节)
            const code = 200;
            const binaryData = Buffer.from('ACK_OK');
            console.log('[/] [Demo007] calling binary ACK, code=' + code + ', data=' + binaryData.toString());
            ackFn(code, binaryData);
        }
    });

    // ─────────────────────────────────────────────────────────────────────
    //  Demo 010:admin_disconnect —— 服务端主动断开,触发客户端重连
    //  客户端可 emit('admin_disconnect') 让服务端主动断开该连接,
    //  模拟服务器侧断线,观察客户端重连策略是否按配置生效
    // ─────────────────────────────────────────────────────────────────────
    socket.on('admin_disconnect', () => {
        console.log('[/] [Demo010] admin_disconnect: server disconnecting socket ' + socket.id);
        socket.disconnect(true);
    });

    // ─────────────────────────────────────────────────────────────────────
    //  通用:ping/pong 连通性测试
    // ─────────────────────────────────────────────────────────────────────
    socket.on('ping_test', (data) => {
        console.log('[/] ping_test received:', data);
        socket.emit('pong_test', { echo: data, ts: Date.now() });
    });

    // ─────────────────────────────────────────────────────────────────────
    //  断开连接日志(Demo 002/012)
    // ─────────────────────────────────────────────────────────────────────
    socket.on('disconnect', (reason) => {
        console.log('[/] 连接断开  socket.id=' + socket.id + '  reason=' + reason);
    });

    socket.on('error', (err) => {
        console.error('[/] socket error:', err);
    });
});

// ═══════════════════════════════════════════════════════════════
//  /test 命名空间 ── Demo 003:Socket命名空间配置与监听
//  客户端 set_nsp('/test') + connect() 后触发 socket_open_listener('/test')
// ═══════════════════════════════════════════════════════════════
const nspTest = io.of('/test');

nspTest.on('connection', (socket) => {
    console.log('\n[/test] 新连接  socket.id=' + socket.id);

    // echo 任意事件,方便 Demo 003 验证命名空间事件收发
    socket.on('echo', (data) => {
        console.log('[/test] echo received:', data);
        socket.emit('echo', data);
    });

    // chat echo(与默认命名空间保持对称)
    socket.on('chat', (...args) => {
        console.log('[/test] chat received:', args);
        socket.emit('chat', ...args);
    });

    socket.on('disconnect', (reason) => {
        console.log('[/test] 连接断开  socket.id=' + socket.id + '  reason=' + reason);
    });
});

// ═══════════════════════════════════════════════════════════════
//  /error-test 命名空间 ── Demo 008:on_error() 触发演示
//  服务端中间件始终调用 next(new Error(...)),向客户端发送 type_error 包,
//  触发客户端 socket 的 on_error 回调(socket.io 协议层错误,非 TCP 失败)
// ═══════════════════════════════════════════════════════════════
const nspErrorTest = io.of('/error-test');

nspErrorTest.use((socket, next) => {
    console.log('\n[/error-test] 收到连接请求,故意拒绝  socket.id=' + socket.id);
    next(new Error('Demo008_intentional_auth_error: 命名空间连接被服务端拒绝'));
});

// connection 回调永远不会执行(已被 middleware 拒绝)
nspErrorTest.on('connection', () => {
});

// ═══════════════════════════════════════════════════════════════
//  启动监听
// ═══════════════════════════════════════════════════════════════
httpServer.listen(PORT, () => {
    console.log('\n服务端已启动,监听端口 ' + PORT);
    console.log('客户端连接地址: http://<本机IP>:' + PORT);
    console.log('\n支持的事件(默认命名空间 /):');
    console.log('  chat          → echo(Demo 004)');
    console.log('  multiEvent    → echo 多参数(Demo 005)');
    console.log('  onceEvent     → echo(Demo 005)');
    console.log('  binaryEvent   → echo Buffer(Demo 006)');
    console.log('  ackEvent      → ACK 回调 {status,echo,timestamp}(Demo 007)');
    console.log('  binaryAckEvent→ 二进制 ACK code=200 + Buffer(ACK_OK)(Demo 007)');
    console.log('  admin_disconnect → 服务端主动断开触发重连(Demo 010)');
    console.log('  ping_test     → pong_test echo(通用连通性)');
    console.log('\n支持的命名空间:');
    console.log('  /test         → echo/chat(Demo 003)');
    console.log('  /error-test   → 始终拒绝命名空间连接,触发客户端 on_error(Demo 008)');
    console.log('='.repeat(60));
});