import { describe, it, expect } from 'vitest'
import {
  loadConfig,
  DEFAULT_CP_URL,
  DEFAULT_TUNNEL_ZONE,
  DEFAULT_PANEL_BIND_PORT,
  DEFAULT_CAPABILITY_SIGN_KEY_PATH,
} from '../src/config.js'

const base = {
  SESSION_SECRET: 'a-sufficiently-long-secret-value',
  BASE_DOMAIN: 'terminal.yaojia.wang',
  OPERATOR_ACCOUNT_ID: 'acct-1',
}

describe('loadConfig', () => {
  it('applies defaults for optional fields', () => {
    const cfg = loadConfig({ ...base } as NodeJS.ProcessEnv)
    expect(cfg.cpUrl).toBe(DEFAULT_CP_URL)
    expect(cfg.tunnelZone).toBe(DEFAULT_TUNNEL_ZONE)
    expect(cfg.panelBindPort).toBe(DEFAULT_PANEL_BIND_PORT)
    expect(cfg.capabilitySignKeyPath).toBe(DEFAULT_CAPABILITY_SIGN_KEY_PATH)
    expect(cfg.panelPassword).toBeUndefined()
  })

  it('reads all provided values and strips trailing slash from CP_URL', () => {
    const cfg = loadConfig({
      ...base,
      PANEL_PASSWORD: 'pw',
      CP_URL: 'http://127.0.0.1:9000/',
      TUNNEL_ZONE: 'z.example',
      PANEL_BIND_PORT: '9999',
    } as NodeJS.ProcessEnv)
    expect(cfg.panelPassword).toBe('pw')
    expect(cfg.cpUrl).toBe('http://127.0.0.1:9000')
    expect(cfg.tunnelZone).toBe('z.example')
    expect(cfg.panelBindPort).toBe(9999)
  })

  it('throws when SESSION_SECRET is missing (fail-closed)', () => {
    const { SESSION_SECRET: _omit, ...rest } = base
    expect(() => loadConfig(rest as NodeJS.ProcessEnv)).toThrow()
  })

  it('throws when SESSION_SECRET is too short', () => {
    expect(() => loadConfig({ ...base, SESSION_SECRET: 'short' } as NodeJS.ProcessEnv)).toThrow()
  })

  it('throws when BASE_DOMAIN is missing', () => {
    const { BASE_DOMAIN: _omit, ...rest } = base
    expect(() => loadConfig(rest as NodeJS.ProcessEnv)).toThrow()
  })

  it('throws when OPERATOR_ACCOUNT_ID is missing', () => {
    const { OPERATOR_ACCOUNT_ID: _omit, ...rest } = base
    expect(() => loadConfig(rest as NodeJS.ProcessEnv)).toThrow()
  })

  it('throws when PANEL_BIND_PORT is out of range', () => {
    expect(() => loadConfig({ ...base, PANEL_BIND_PORT: '70000' } as NodeJS.ProcessEnv)).toThrow()
  })

  it('accepts loopback CP_URL hosts (127.0.0.0/8, ::1, localhost)', () => {
    for (const url of ['http://127.0.0.1:8080', 'http://127.9.9.9:1', 'http://[::1]:8080', 'http://localhost:8080']) {
      expect(loadConfig({ ...base, CP_URL: url } as NodeJS.ProcessEnv).cpUrl).toBe(url)
    }
  })

  it('throws when CP_URL host is not loopback (anti-SSRF, fail-closed)', () => {
    for (const url of ['http://evil.example.com:8080', 'http://169.254.169.254/', 'http://10.0.0.5:8080', 'http://8.8.8.8']) {
      expect(() => loadConfig({ ...base, CP_URL: url } as NodeJS.ProcessEnv)).toThrow()
    }
  })
})
