/**
 * @vitest-environment node
 */
import fs from 'node:fs/promises'
import os from 'node:os'
import path from 'node:path'
import { afterEach, describe, expect, it } from 'vitest'
import { loadDevProxyConfig, parseDevProxyCliArgs, resolveDevProxyServerOptions } from './config'

const tempDirs: string[] = []

const createTempDir = async () => {
  const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), 'dev-proxy-test-'))
  tempDirs.push(tempDir)
  return tempDir
}

describe('dev proxy config', () => {
  afterEach(async () => {
    delete process.env.DEV_PROXY_TEST_PORT
    delete process.env.DEV_PROXY_TEST_TARGET

    await Promise.all(tempDirs.splice(0).map(tempDir => fs.rm(tempDir, {
      force: true,
      recursive: true,
    })))
  })

  // Scenario: CLI options should support both inline and separated values.
  it('should parse proxy CLI options', () => {
    // Act
    const options = parseDevProxyCliArgs([
      '--config=./dev-proxy.config.ts',
      '--env-file',
      './.env.proxy',
      '--host',
      '0.0.0.0',
      '--port',
      '8083',
      '--no-watch',
    ])

    // Assert
    expect(options).toEqual({
      config: './dev-proxy.config.ts',
      envFile: './.env.proxy',
      host: '0.0.0.0',
      port: '8083',
      watch: false,
    })
  })

  // Scenario: removed target shortcuts should fail instead of silently doing the wrong thing.
  it('should reject unsupported target shortcuts', () => {
    // Assert
    expect(() => parseDevProxyCliArgs(['--target', 'enterprise'])).toThrow('Unsupported dev proxy option')
  })

  // Scenario: package manager argument separators should not be treated as proxy options.
  it('should ignore package manager argument separators', () => {
    // Act
    const options = parseDevProxyCliArgs(['--config', './dev-proxy.config.ts', '--', '--help'])

    // Assert
    expect(options).toEqual({
      config: './dev-proxy.config.ts',
      help: true,
    })
  })

  // Scenario: CLI host and port should override config defaults.
  it('should resolve server options with CLI overrides', () => {
    // Act
    const options = resolveDevProxyServerOptions({
      host: '127.0.0.1',
      port: 5001,
    }, {
      host: '0.0.0.0',
      port: '9002',
    })

    // Assert
    expect(options).toEqual({
      host: '0.0.0.0',
      port: 9002,
    })
  })

  // Scenario: TS config files should load through c12.
  it('should load a TypeScript config file', async () => {
    // Arrange
    const tempDir = await createTempDir()
    await fs.writeFile(path.join(tempDir, 'dev-proxy.config.ts'), `
      export default {
        server: { host: '127.0.0.1', port: 7777 },
        routes: [{ paths: ['/api', '/files'], target: 'https://api.example.com' }],
      }
    `)

    // Act
    const config = await loadDevProxyConfig('dev-proxy.config.ts', tempDir)

    // Assert
    expect(config.server).toEqual({
      host: '127.0.0.1',
      port: 7777,
    })
    expect(config.routes).toEqual([
      {
        paths: ['/api', '/files'],
        target: 'https://api.example.com',
      },
    ])
  })

  // Scenario: env files should be loaded before the TypeScript config is evaluated.
  it('should load a TypeScript config file with env file values', async () => {
    // Arrange
    const tempDir = await createTempDir()
    await fs.writeFile(path.join(tempDir, '.env.proxy'), [
      'DEV_PROXY_TEST_PORT=7788',
      'DEV_PROXY_TEST_TARGET=https://env.example.com',
    ].join('\n'))
    await fs.writeFile(path.join(tempDir, 'dev-proxy.config.ts'), `
      export default {
        server: { port: Number(process.env.DEV_PROXY_TEST_PORT) },
        routes: [{ paths: '/api', target: process.env.DEV_PROXY_TEST_TARGET }],
      }
    `)

    // Act
    const config = await loadDevProxyConfig('dev-proxy.config.ts', tempDir, {
      envFile: '.env.proxy',
    })

    // Assert
    expect(config.server).toEqual({
      port: 7788,
    })
    expect(config.routes).toEqual([
      {
        paths: '/api',
        target: 'https://env.example.com',
      },
    ])
  })
})
