import { mkdtemp, writeFile } from 'node:fs/promises'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import { afterEach, describe, expect, test } from 'bun:test'
import { createTempHome } from '../helpers/temp-env'
import { startFakeRegistry } from '../helpers/fake-registry'
import { runCli } from '../helpers/run-cli'

let registry: Awaited<ReturnType<typeof startFakeRegistry>> | undefined

afterEach(() => {
  registry?.stop()
  registry = undefined
})

async function login(env: { home: string }, registryUrl: string) {
  const result = await runCli(['login', '--registry', registryUrl, '--token', 'sk_ok'], {
    HOME: env.home,
    USERPROFILE: env.home
  })
  if (result.exitCode !== 0) {
    throw new Error(`login failed: ${result.stderr}`)
  }
}

async function makeTempDir(...files: Array<[string, string]>) {
  const dir = await mkdtemp(join(tmpdir(), 'skillhub-dryrun-'))
  for (const [name, content] of files) {
    await writeFile(join(dir, name), content)
  }
  return dir
}

describe('publish --dry-run', () => {
  test('calls validate endpoint and reports success', async () => {
    const env = await createTempHome()
    registry = await startFakeRegistry({ token: 'sk_ok' })
    await login(env, registry.url)

    const dir = await makeTempDir(['SKILL.md', '---\nname: my-skill\ndescription: A test\n---\n# Hello'])
    const result = await runCli(['publish', dir, '--dry-run', '--registry', registry.url], {
      HOME: env.home,
      USERPROFILE: env.home
    })

    expect(result.exitCode).toBe(0)
    expect(result.stdout).toContain('Validation passed')
    expect(registry.received.validate).not.toBeNull()
    expect(registry.received.validate!.namespace).toBe('global')
    expect(registry.received.publish).toBeNull()
  })

  test('--dry-run with --json returns structured response on warnings (valid=false)', async () => {
    const env = await createTempHome()
    registry = await startFakeRegistry({
      token: 'sk_ok',
      dryRunResponse: {
        valid: false,
        errors: [],
        warnings: ['Disallowed file extension: data.bin'],
        resolvedSlug: 'my-skill',
        resolvedVersion: '2.0.0'
      }
    })
    await login(env, registry.url)

    const dir = await makeTempDir(['SKILL.md', '---\nname: my-skill\ndescription: test\n---\n'])
    const result = await runCli(['publish', dir, '--dry-run', '--json', '--registry', registry.url], {
      HOME: env.home,
      USERPROFILE: env.home
    })

    expect(result.exitCode).toBe(6)
    const json = JSON.parse(result.stdout)
    expect(json.valid).toBe(false)
    expect(json.resolvedSlug).toBe('my-skill')
    expect(json.resolvedVersion).toBe('2.0.0')
    expect(json.warnings).toContain('Disallowed file extension: data.bin')
  })

  test('--dry-run reports validation errors', async () => {
    const env = await createTempHome()
    registry = await startFakeRegistry({
      token: 'sk_ok',
      dryRunResponse: {
        valid: false,
        errors: ['Missing required file: SKILL.md at root'],
        warnings: [],
        resolvedSlug: null,
        resolvedVersion: null
      }
    })
    await login(env, registry.url)

    const dir = await makeTempDir(['README.md', '# No SKILL.md here'])
    const result = await runCli(['publish', dir, '--dry-run', '--registry', registry.url], {
      HOME: env.home,
      USERPROFILE: env.home
    })

    expect(result.exitCode).toBe(6)
    expect(result.stdout).toContain('Validation failed')
    expect(result.stdout).toContain('Missing required file: SKILL.md at root')
  })

  test('--dry-run does not actually publish', async () => {
    const env = await createTempHome()
    registry = await startFakeRegistry({ token: 'sk_ok' })
    await login(env, registry.url)

    const dir = await makeTempDir(['SKILL.md', '---\nname: test\ndescription: test\n---\n'])
    await runCli(['publish', dir, '--dry-run', '--registry', registry.url], {
      HOME: env.home,
      USERPROFILE: env.home
    })

    expect(registry.received.publish).toBeNull()
  })

  test('--dry-run respects --namespace', async () => {
    const env = await createTempHome()
    registry = await startFakeRegistry({ token: 'sk_ok' })
    await login(env, registry.url)

    const dir = await makeTempDir(['SKILL.md', '---\nname: test\ndescription: test\n---\n'])
    await runCli(['publish', dir, '--dry-run', '--namespace', 'myteam', '--registry', registry.url], {
      HOME: env.home,
      USERPROFILE: env.home
    })

    expect(registry.received.validate!.namespace).toBe('myteam')
  })

  test('--dry-run forwards --visibility to server', async () => {
    const env = await createTempHome()
    registry = await startFakeRegistry({ token: 'sk_ok' })
    await login(env, registry.url)

    const dir = await makeTempDir(['SKILL.md', '---\nname: test\ndescription: test\n---\n'])
    await runCli(['publish', dir, '--dry-run', '--visibility', 'private', '--registry', registry.url], {
      HOME: env.home,
      USERPROFILE: env.home
    })

    expect(registry.received.validate!.visibility).toBe('PRIVATE')
  })

  test('--dry-run requires authentication', async () => {
    const env = await createTempHome()
    registry = await startFakeRegistry({ token: 'sk_ok' })

    const dir = await makeTempDir(['SKILL.md', '---\nname: test\ndescription: test\n---\n'])
    const result = await runCli(['publish', dir, '--dry-run', '--registry', registry.url], {
      HOME: env.home,
      USERPROFILE: env.home
    })

    expect(result.exitCode).toBe(2)
    expect(result.stderr).toContain('authentication')
  })

  test('--dry-run surfaces the public message and request ID on a structured 403', async () => {
    const env = await createTempHome()
    registry = await startFakeRegistry({ token: 'sk_ok', failures: { validate: 'forbidden' } })
    await login(env, registry.url)

    const dir = await makeTempDir(['SKILL.md', '---\nname: test\ndescription: test\n---\n'])
    const result = await runCli(['publish', dir, '--dry-run', '--registry', registry.url], {
      HOME: env.home,
      USERPROFILE: env.home
    })

    expect(result.exitCode).toBe(2)
    expect(result.stderr).toContain('scope')
    expect(result.stderr).toContain('Request ID: req-test-forbidden')
  })

  test('--dry-run uses a neutral fallback without leaking an unstructured 403 body', async () => {
    const env = await createTempHome()
    registry = await startFakeRegistry({ token: 'sk_ok', failures: { validate: 'forbidden_unstructured' } })
    await login(env, registry.url)

    const dir = await makeTempDir(['SKILL.md', '---\nname: test\ndescription: test\n---\n'])
    const result = await runCli(['publish', dir, '--dry-run', '--registry', registry.url], {
      HOME: env.home,
      USERPROFILE: env.home
    })

    expect(result.exitCode).toBe(2)
    expect(result.stderr).toContain('access denied')
    expect(result.stderr).not.toContain('sensitive proxy denial')
  })
})
