import { describe, it, expect, beforeEach, afterEach } from 'vitest'
import { existsSync, writeFileSync, mkdirSync, unlinkSync } from 'node:fs'
import { join } from 'node:path'
import { tmpdir } from 'node:os'
import {
  TRUSTSTORE_PASSWORD,
  DEFAULT_COT_PORT,
  getCotPort,
  COT_TLS_REQUIRED_MESSAGE,
  getCotSslPaths,
  buildP12FromCertPath,
} from '../../server/utils/cotSsl.js'
import { withTemporaryEnv } from '../helpers/env.js'

describe('cotSsl', () => {
  const testPaths = {
    testCertDir: null,
    testCertPath: null,
    testKeyPath: null,
  }

  beforeEach(() => {
    testPaths.testCertDir = join(tmpdir(), `kestrelos-test-${Date.now()}`)
    mkdirSync(testPaths.testCertDir, { recursive: true })
    testPaths.testCertPath = join(testPaths.testCertDir, 'cert.pem')
    testPaths.testKeyPath = join(testPaths.testCertDir, 'key.pem')
    writeFileSync(testPaths.testCertPath, '-----BEGIN CERTIFICATE-----\nTEST\n-----END CERTIFICATE-----\n')
    writeFileSync(testPaths.testKeyPath, '-----BEGIN PRIVATE KEY-----\nTEST\n-----END PRIVATE KEY-----\n')
  })

  afterEach(() => {
    try {
      if (existsSync(testPaths.testCertPath)) unlinkSync(testPaths.testCertPath)
      if (existsSync(testPaths.testKeyPath)) unlinkSync(testPaths.testKeyPath)
    }
    catch {
      // Ignore cleanup errors
    }
  })

  describe('constants', () => {
    it.each([
      ['TRUSTSTORE_PASSWORD', TRUSTSTORE_PASSWORD, 'kestrelos'],
      ['DEFAULT_COT_PORT', DEFAULT_COT_PORT, 8089],
    ])('exports %s', (name, value, expected) => {
      expect(value).toBe(expected)
    })

    it('exports COT_TLS_REQUIRED_MESSAGE', () => {
      expect(COT_TLS_REQUIRED_MESSAGE).toContain('SSL')
    })
  })

  describe('getCotPort', () => {
    it.each([
      [{ COT_PORT: undefined }, DEFAULT_COT_PORT],
      [{ COT_PORT: '9999' }, 9999],
      [{ COT_PORT: '8080' }, 8080],
    ])('returns correct port for env: %j', (env, expected) => {
      withTemporaryEnv(env, () => {
        expect(getCotPort()).toBe(expected)
      })
    })
  })

  describe('getCotSslPaths', () => {
    it('returns paths from env vars when available, otherwise checks default locations', () => {
      withTemporaryEnv({ COT_SSL_CERT: undefined, COT_SSL_KEY: undefined }, () => {
        const result = getCotSslPaths()
        if (result !== null) {
          expect(result).toMatchObject({
            certPath: expect.any(String),
            keyPath: expect.any(String),
          })
        }
        else {
          expect(result).toBeNull()
        }
      })
    })

    it('returns paths from COT_SSL_CERT and COT_SSL_KEY env vars', () => {
      withTemporaryEnv({ COT_SSL_CERT: testPaths.testCertPath, COT_SSL_KEY: testPaths.testKeyPath }, () => {
        expect(getCotSslPaths()).toEqual({ certPath: testPaths.testCertPath, keyPath: testPaths.testKeyPath })
      })
    })

    it('returns paths from config parameter when env vars not set', () => {
      withTemporaryEnv({ COT_SSL_CERT: undefined, COT_SSL_KEY: undefined }, () => {
        const config = { cotSslCert: testPaths.testCertPath, cotSslKey: testPaths.testKeyPath }
        expect(getCotSslPaths(config)).toEqual({ certPath: testPaths.testCertPath, keyPath: testPaths.testKeyPath })
      })
    })

    it('prefers env vars over config parameter', () => {
      withTemporaryEnv({ COT_SSL_CERT: testPaths.testCertPath, COT_SSL_KEY: testPaths.testKeyPath }, () => {
        const config = { cotSslCert: '/other/cert.pem', cotSslKey: '/other/key.pem' }
        expect(getCotSslPaths(config)).toEqual({ certPath: testPaths.testCertPath, keyPath: testPaths.testKeyPath })
      })
    })

    it('returns paths from config even if files do not exist', () => {
      withTemporaryEnv({ COT_SSL_CERT: undefined, COT_SSL_KEY: undefined }, () => {
        const result = getCotSslPaths({ cotSslCert: '/nonexistent/cert.pem', cotSslKey: '/nonexistent/key.pem' })
        expect(result).toEqual({ certPath: '/nonexistent/cert.pem', keyPath: '/nonexistent/key.pem' })
      })
    })
  })

  describe('buildP12FromCertPath', () => {
    it('throws error when cert file does not exist', () => {
      expect(() => {
        buildP12FromCertPath('/nonexistent/cert.pem', 'password')
      }).toThrow()
    })

    it('throws error when openssl command fails', () => {
      const invalidCertPath = join(testPaths.testCertDir, 'invalid.pem')
      writeFileSync(invalidCertPath, 'invalid cert content')
      expect(() => {
        buildP12FromCertPath(invalidCertPath, 'password')
      }).toThrow()
    })

    it('cleans up temp file on error', () => {
      const invalidCertPath = join(testPaths.testCertDir, 'invalid.pem')
      writeFileSync(invalidCertPath, 'invalid cert content')
      try {
        buildP12FromCertPath(invalidCertPath, 'password')
      }
      catch {
        // Expected to throw
      }
      // Function should clean up on error - test passes if no exception during cleanup
      expect(true).toBe(true)
    })
  })
})
