import { afterEach, describe, expect, it } from 'vitest'
import { execFile } from 'node:child_process'
import fs from 'node:fs'
import path from 'node:path'
import { promisify } from 'node:util'

const execFileAsync = promisify(execFile)
const coreRoot = path.resolve(import.meta.dirname, '../..')
const distRoot = path.resolve(coreRoot, 'dist')
const packageJsonPath = path.resolve(coreRoot, 'package.json')
const tempRoots: string[] = []

const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8')) as {
  version: string
  type: string
  sideEffects?: boolean | string[]
  publishConfig?: {
    name?: string
    exports?: Record<string, string | { types?: string; import?: string }>
  }
}

function packagePath(target: string): string {
  return path.resolve(coreRoot, target.replace(/^\.\//, ''))
}

function createConsumerPackage(): string {
  const tempRoot = fs.mkdtempSync(path.join(coreRoot, '.tmp-published-core-'))
  tempRoots.push(tempRoot)

  const packageRoot = path.join(tempRoot, 'node_modules', '@elevasis', 'core')
  fs.mkdirSync(packageRoot, { recursive: true })
  fs.cpSync(distRoot, path.join(packageRoot, 'dist'), { recursive: true })
  fs.writeFileSync(
    path.join(packageRoot, 'package.json'),
    JSON.stringify(
      {
        name: packageJson.publishConfig?.name,
        version: packageJson.version,
        type: packageJson.type,
        sideEffects: packageJson.sideEffects,
        exports: packageJson.publishConfig?.exports
      },
      null,
      2
    )
  )

  return tempRoot
}

describe.skipIf(!fs.existsSync(distRoot))('published @elevasis/core artifact', { timeout: 30_000 }, () => {
  afterEach(() => {
    for (const tempRoot of tempRoots.splice(0)) {
      fs.rmSync(tempRoot, { recursive: true, force: true })
    }
  })

  it('has publish-ready runtime and declaration artifacts for every public export', () => {
    expect(packageJson.publishConfig?.name).toBe('@elevasis/core')

    for (const [subpath, entry] of Object.entries(packageJson.publishConfig?.exports ?? {})) {
      if (typeof entry === 'string') {
        expect(entry, `${subpath} must point at dist`).toMatch(/^\.\//)
        expect(fs.existsSync(packagePath(entry)), `${subpath} target does not exist: ${entry}`).toBe(true)
        continue
      }

      expect(entry.types, `${subpath} is missing a types condition`).toBeTruthy()
      expect(entry.import, `${subpath} is missing an import condition`).toBeTruthy()
      expect(fs.existsSync(packagePath(entry.types ?? '')), `${subpath} types do not exist: ${entry.types}`).toBe(true)
      expect(fs.existsSync(packagePath(entry.import ?? '')), `${subpath} runtime does not exist: ${entry.import}`).toBe(
        true
      )
    }
  })

  it('resolves public runtime subpaths from a temp consumer package', async () => {
    const consumerRoot = createConsumerPackage()
    const smokeScript = `
      const core = await import('@elevasis/core')
      const auth = await import('@elevasis/core/auth')
      const orgModel = await import('@elevasis/core/organization-model')
      const entities = await import('@elevasis/core/entities')
      const knowledge = await import('@elevasis/core/knowledge')

      if (!core.DEFAULT_ORGANIZATION_MODEL) throw new Error('missing DEFAULT_ORGANIZATION_MODEL')
      if (!auth.AccessKeys) throw new Error('missing AccessKeys')
      if (typeof orgModel.defineOrganizationModel !== 'function') throw new Error('missing defineOrganizationModel')
      if (!entities.BaseProjectSchema) throw new Error('missing BaseProjectSchema')
      if (typeof knowledge.parsePath !== 'function') throw new Error('missing parsePath')
    `

    await execFileAsync(process.execPath, ['--input-type=module', '--eval', smokeScript], {
      cwd: consumerRoot,
      maxBuffer: 1024 * 1024
    })
  })
})
