import { describe, expect, it } from 'vitest'
import { buildNativeFrpcToml, type NativeFrpcOptions } from '../src/transport/frpcToml.js'

const BASE: NativeFrpcOptions = {
  subdomain: 'alice',
  localPort: 3000,
  authToken: 'super-secret-token',
  certFile: '/home/alice/.web-terminal-agent/frpc.cert.pem',
  keyFile: '/home/alice/.web-terminal-agent/frpc.key.pem',
  trustedCaFile: '/home/alice/.web-terminal-agent/frps-ctrl-ca.pem',
}

describe('buildNativeFrpcToml (B2h)', () => {
  it('emits the native-tunnel server/port/tls keys (PLAN_NATIVE_TUNNEL §4)', () => {
    const toml = buildNativeFrpcToml(BASE)
    expect(toml).toContain('serverAddr = "8.138.1.192"')
    expect(toml).toContain('serverPort = 443')
    expect(toml).toContain('transport.tls.enable = true')
    expect(toml).toContain('transport.tls.serverName = "frp.terminal.yaojia.wang"')
    expect(toml).toContain('transport.tls.disableCustomTLSFirstByte = true')
    expect(toml).toContain(`transport.tls.certFile = "${BASE.certFile}"`)
    expect(toml).toContain(`transport.tls.keyFile = "${BASE.keyFile}"`)
    expect(toml).toContain(`transport.tls.trustedCaFile = "${BASE.trustedCaFile}"`)
    expect(toml).toContain('auth.method = "token"')
    expect(toml).toContain('auth.token = "super-secret-token"')
  })

  it('emits a well-formed [[proxies]] http block bound to loopback', () => {
    const toml = buildNativeFrpcToml(BASE)
    expect(toml).toContain('[[proxies]]')
    expect(toml).toContain('type = "http"')
    expect(toml).toContain('subdomain = "alice"')
    expect(toml).toContain('localIP = "127.0.0.1"')
    expect(toml).toContain('localPort = 3000')
    // the proxy block comes after the server/tls preamble
    expect(toml.indexOf('[[proxies]]')).toBeGreaterThan(toml.indexOf('serverAddr'))
  })

  it('does NOT emit the retired v0.8 [common]/tls_enable shape', () => {
    const toml = buildNativeFrpcToml(BASE)
    expect(toml).not.toContain('[common]')
    expect(toml).not.toContain('tls_enable')
  })

  it('honours a configurable serverAddr (default 8.138.1.192)', () => {
    expect(buildNativeFrpcToml(BASE)).toContain('serverAddr = "8.138.1.192"')
    expect(buildNativeFrpcToml({ ...BASE, serverAddr: '10.9.8.7' })).toContain(
      'serverAddr = "10.9.8.7"',
    )
  })

  it('THROWS when localIP is non-loopback (anti-SSRF hard invariant)', () => {
    expect(() => buildNativeFrpcToml({ ...BASE, localIP: '10.0.0.5' })).toThrow(/loopback/i)
    expect(() => buildNativeFrpcToml({ ...BASE, localIP: '0.0.0.0' })).toThrow(/loopback/i)
    expect(() => buildNativeFrpcToml({ ...BASE, localIP: '192.168.1.9' })).toThrow(/loopback/i)
  })

  it('accepts the three explicit loopback localIP forms', () => {
    for (const ip of ['127.0.0.1', '::1', 'localhost']) {
      expect(() => buildNativeFrpcToml({ ...BASE, localIP: ip })).not.toThrow()
    }
  })

  it('rejects an empty or non-label-safe subdomain', () => {
    expect(() => buildNativeFrpcToml({ ...BASE, subdomain: '' })).toThrow(/subdomain/i)
    expect(() => buildNativeFrpcToml({ ...BASE, subdomain: 'has space' })).toThrow(/subdomain/i)
    expect(() => buildNativeFrpcToml({ ...BASE, subdomain: '-bad' })).toThrow(/subdomain/i)
    expect(() => buildNativeFrpcToml({ ...BASE, subdomain: 'bad-' })).toThrow(/subdomain/i)
    expect(() => buildNativeFrpcToml({ ...BASE, subdomain: 'a'.repeat(64) })).toThrow(/subdomain/i)
    expect(() => buildNativeFrpcToml({ ...BASE, subdomain: 'a/b' })).toThrow(/subdomain/i)
  })

  it('rejects an out-of-range or non-integer localPort', () => {
    expect(() => buildNativeFrpcToml({ ...BASE, localPort: 0 })).toThrow(/port/i)
    expect(() => buildNativeFrpcToml({ ...BASE, localPort: 70000 })).toThrow(/port/i)
    expect(() => buildNativeFrpcToml({ ...BASE, localPort: 3000.5 })).toThrow(/port/i)
    expect(() => buildNativeFrpcToml({ ...BASE, localPort: -1 })).toThrow(/port/i)
  })

  it('rejects missing keystore paths and an empty auth token', () => {
    expect(() => buildNativeFrpcToml({ ...BASE, certFile: '' })).toThrow(/certFile/i)
    expect(() => buildNativeFrpcToml({ ...BASE, keyFile: '' })).toThrow(/keyFile/i)
    expect(() => buildNativeFrpcToml({ ...BASE, trustedCaFile: '' })).toThrow(/trustedCaFile/i)
    expect(() => buildNativeFrpcToml({ ...BASE, authToken: '' })).toThrow(/token/i)
  })

  it('escapes backslashes and quotes in Windows-style paths (valid TOML basic string)', () => {
    const winCert = 'C:\\Users\\alice\\.web-terminal-agent\\frpc.cert.pem'
    const toml = buildNativeFrpcToml({ ...BASE, certFile: winCert })
    expect(toml).toContain(
      'transport.tls.certFile = "C:\\\\Users\\\\alice\\\\.web-terminal-agent\\\\frpc.cert.pem"',
    )
  })

  it('rejects control characters in paths/token (TOML-injection guard)', () => {
    expect(() => buildNativeFrpcToml({ ...BASE, authToken: 'a\nb' })).toThrow()
    expect(() => buildNativeFrpcToml({ ...BASE, certFile: 'a\nb' })).toThrow()
    expect(() => buildNativeFrpcToml({ ...BASE, keyFile: 'a"b\nq' })).toThrow()
  })
})
