import { describe, it, expect } from 'vitest';

import { composeInboundTag, isAutoInboundTag, type InboundTagInput } from '@/lib/xray/inbound-tag';

// Parity with web/service/port_conflict.go TestInboundTransports: the L4 suffix
// the tag encodes must match the Go service so the form preview agrees with the
// tag the backend re-derives on save.
describe('composeInboundTag transport suffix parity', () => {
  const base = (over: Partial<InboundTagInput>): InboundTagInput => ({
    port: 443,
    nodeId: null,
    protocol: 'vless',
    ...over,
  });

  const cases: Array<[string, InboundTagInput, string]> = [
    ['vless tcp', base({ streamSettings: { network: 'tcp' } }), 'in-443-tcp'],
    ['vless ws (still tcp)', base({ streamSettings: { network: 'ws' } }), 'in-443-tcp'],
    ['vless kcp is udp', base({ streamSettings: { network: 'kcp' } }), 'in-443-udp'],
    ['vless quic is udp', base({ streamSettings: { network: 'quic' } }), 'in-443-udp'],
    ['vless empty stream defaults tcp', base({}), 'in-443-tcp'],
    ['vmess tcp', base({ protocol: 'vmess', streamSettings: { network: 'tcp' } }), 'in-443-tcp'],
    ['trojan grpc is tcp', base({ protocol: 'trojan', streamSettings: { network: 'grpc' } }), 'in-443-tcp'],
    ['hysteria forced udp', base({ protocol: 'hysteria', streamSettings: { network: 'tcp' } }), 'in-443-udp'],
    ['wireguard forced udp', base({ protocol: 'wireguard' }), 'in-443-udp'],
    ['shadowsocks tcp,udp', base({ protocol: 'shadowsocks', settings: { network: 'tcp,udp' } }), 'in-443-tcpudp'],
    ['shadowsocks udp only', base({ protocol: 'shadowsocks', settings: { network: 'udp' } }), 'in-443-udp'],
    ['shadowsocks tcp only', base({ protocol: 'shadowsocks', settings: { network: 'tcp' } }), 'in-443-tcp'],
    ['mixed udp on', base({ protocol: 'mixed', streamSettings: { network: 'tcp' }, settings: { udp: true } }), 'in-443-tcpudp'],
    ['mixed udp off', base({ protocol: 'mixed', streamSettings: { network: 'tcp' }, settings: { udp: false } }), 'in-443-tcp'],
    ['tunnel allowedNetwork udp', base({ protocol: 'tunnel', settings: { allowedNetwork: 'udp' } }), 'in-443-udp'],
  ];

  it.each(cases)('%s', (_name, input, want) => {
    expect(composeInboundTag(input)).toBe(want);
  });

  it('ignores the listen address and adds the node prefix', () => {
    expect(composeInboundTag(base({ port: 8443, streamSettings: { network: 'tcp' } })))
      .toBe('in-8443-tcp');
    expect(composeInboundTag(base({ nodeId: 1, port: 443, streamSettings: { network: 'tcp' } })))
      .toBe('n1-in-443-tcp');
  });
});

// Parity with TestIsAutoGeneratedTag.
describe('isAutoInboundTag', () => {
  const input: InboundTagInput = {
    port: 443, nodeId: null, protocol: 'vless', streamSettings: { network: 'tcp' },
  };

  it('recognises canonical, dedup-suffixed and empty as auto', () => {
    expect(isAutoInboundTag('in-443-tcp', input)).toBe(true);
    expect(isAutoInboundTag('in-443-tcp-2', input)).toBe(true);
    expect(isAutoInboundTag('', input)).toBe(true);
  });

  it('treats custom / stale / malformed-suffix tags as not auto', () => {
    expect(isAutoInboundTag('my-custom', input)).toBe(false);
    expect(isAutoInboundTag('in-8443-tcp', input)).toBe(false);
    expect(isAutoInboundTag('in-443-tcp-x', input)).toBe(false);
    expect(isAutoInboundTag('in-443-tcp-', input)).toBe(false);
  });
});
