import { describe, test, expect } from 'vitest'
import { createMemoryStores } from '../src/store/memory.js'
import { createHostRegistry } from '../src/registry/hosts.js'
import { createLeafRenewer } from '../src/ca/rotate.js'
import { LeafSignError } from '../src/ca/sign.js'
import { buildCaSigner, inProcessCaSigner, type KmsResolver } from '../src/boot/ca-wiring.js'
import { buildCsr } from '../src/ca/csr.js'
import { fingerprint } from '../src/ca/fingerprint.js'
import { generateEd25519 } from '../src/util/crypto.js'
import { loadEnv } from '../src/env.js'
import { bytesToBase64 } from '../src/util/bytes.js'
import { randomBytes } from 'node:crypto'

const ACCOUNT = '11111111-1111-4111-8111-111111111111'

async function boundHost() {
  const stores = createMemoryStores()
  const hosts = createHostRegistry({ hosts: stores.hosts })
  const { publicKeyRaw, privateKey } = generateEd25519()
  const host = await hosts.bindHost({ accountId: ACCOUNT, subdomain: 'alice', agentPubkey: publicKeyRaw, enrollFpr: fingerprint(publicKeyRaw) })
  const renewer = createLeafRenewer({ hosts: stores.hosts, signer: inProcessCaSigner(), caChainDer: [] })
  return { stores, hosts, host, publicKeyRaw, privateKey, renewer }
}

describe('T15 renewHostLeaf (INV14)', () => {
  test('active host + valid CSR → fresh short-TTL leaf (same subject pubkey)', async () => {
    const { host, publicKeyRaw, privateKey, renewer } = await boundHost()
    const { cert } = await renewer.renewHostLeaf(host.hostId, buildCsr(privateKey, publicKeyRaw))
    const certJson = JSON.parse(Buffer.from(cert).toString())
    const tbs = JSON.parse(Buffer.from(certJson.tbs, 'base64').toString())
    expect(tbs.subjectSpki).toBe(Buffer.from(publicKeyRaw).toString('base64'))
    expect(tbs.renewed).toBe(true)
  })

  test('CSR proof-of-possession fails → reject even for an active host', async () => {
    const { host, publicKeyRaw, renewer } = await boundHost()
    const forged = new Uint8Array(96)
    forged.set(publicKeyRaw, 0)
    forged.set(randomBytes(64), 32) // garbage signature
    await expect(renewer.renewHostLeaf(host.hostId, forged)).rejects.toBeInstanceOf(LeafSignError)
  })

  test('revoked host cannot renew (INV12+INV14 loop)', async () => {
    const { hosts, host, publicKeyRaw, privateKey, renewer } = await boundHost()
    await hosts.setHostStatus(host.hostId, 'revoked')
    await expect(renewer.renewHostLeaf(host.hostId, buildCsr(privateKey, publicKeyRaw))).rejects.toBeInstanceOf(LeafSignError)
  })
})

describe('T1/T15 buildCaSigner startup key-policy check (INV9, §3.1)', () => {
  const env = () =>
    loadEnv({
      PG_URL: 'postgres://u:p@localhost:5432/cp',
      REDIS_URL: 'redis://localhost:6379',
      CAPABILITY_SIGN_PUBKEY_B64: bytesToBase64(new Uint8Array(32).fill(1)),
      CA_INTERMEDIATE_KMS_KEY_REF: 'kms://key/intermediate',
      CA_INTERMEDIATE_CERT_PATH: '/etc/cp/int.pem',
      NODE_MTLS_TRUST_BUNDLE_PATH: '/etc/cp/node-ca.pem',
      BASE_DOMAIN: 'term.example.com',
    })

  test('fails fast when the KMS key ref cannot be resolved', async () => {
    const resolver: KmsResolver = { async resolve() { throw new Error('no such key') } }
    await expect(buildCaSigner(env(), resolver)).rejects.toThrow(/could not be resolved/)
  })

  test('fails fast when the key policy is broader than the service principal', async () => {
    const resolver: KmsResolver = {
      async resolve() {
        return { signer: inProcessCaSigner(), policyRestrictedToServicePrincipal: false }
      },
    }
    await expect(buildCaSigner(env(), resolver)).rejects.toThrow(/policy is broader/)
  })

  test('succeeds when policy is restricted', async () => {
    const resolver: KmsResolver = {
      async resolve() {
        return { signer: inProcessCaSigner(), policyRestrictedToServicePrincipal: true }
      },
    }
    await expect(buildCaSigner(env(), resolver)).resolves.toBeDefined()
  })
})
