/**
 * Assembled-app regression: a parent-only read-only override is seeded into
 * its child log and confines a real write under a wider deployment default.
 */

import { readFile, readdir, writeFile } from 'node:fs/promises'
import { join } from 'node:path'
import { fileURLToPath } from 'node:url'
import { Context } from 'cordis'
import { normalizeSessionLog, scrubRequestHeaders, type NormalizeContext } from '@deepseek-ai/dsh-acp-snapshot'
import { LOADER_SMOKE_TEST_TIMEOUT_MS, runLoaderSmoke } from '@deepseek-ai/dsh-loader-smoke'
import { createUserMessage } from '@deepseek-ai/dsh-llm'
import SessionStore, { SESSION_FORMAT_VERSION, SessionId, type SessionEvent, type SessionHeader } from '@deepseek-ai/dsh-session'
import SessionPersistenceJsonl from '@deepseek-ai/dsh-session-persistence-jsonl'
import { describe, expect, it } from 'vitest'

const fixtureDir = fileURLToPath(new URL('./subagent-inheritance-snapshots/parent-override', import.meta.url))
const replayOverride = join(fixtureDir, 'replay.override.json')
const childReplay = join(fixtureDir, 'child.replay.jsonl')
const parentExpected = join(fixtureDir, 'parent.expected.jsonl')
const childExpected = join(fixtureDir, 'child.expected.jsonl')
const configPath = fileURLToPath(new URL('../subagent-inheritance.cordis.snapshot.yml', import.meta.url))
const binScript = fileURLToPath(new URL('../../../packages/examples/cli-demo/src/bin.ts', import.meta.url))
const tsconfigPath = fileURLToPath(new URL('../../../tsconfig.json', import.meta.url))
const sessionId = SessionId('subagent-inheritance-parent')
const refreshing = process.env.DSH_SNAPSHOT === 'refresh'
const task = 'Delegate the write probe to a subagent.'

/** Seed a completed parent turn with the only read-only fact in the app. */
async function seedReadOnlyParent(root: string, cwd: string): Promise<void> {
  const ctx = new Context()
  await ctx.plugin(SessionStore)
  await ctx.plugin(SessionPersistenceJsonl, { root, compression: 'none' })
  const meta: SessionHeader = {
    version: SESSION_FORMAT_VERSION,
    id: sessionId,
    createdAt: 1,
    cwd,
    delegationDepth: 0,
  }
  const events: SessionEvent[] = [
    { type: 'turn/start', seq: 0, time: 10, data: { turn: 1, trigger: { kind: 'message', source: { kind: 'user' } } } },
    { type: 'user/message', seq: 1, time: 11, data: createUserMessage({ content: [{ type: 'text', text: 'Tighten this session to read-only.' }], source: { kind: 'user' } }), surfaceOp: 'append' },
    { type: 'sandbox/mode', seq: 2, time: 12, data: { mode: 'read-only' } },
    { type: 'turn/end', seq: 3, time: 13, data: { turn: 1, reason: { kind: 'completed' } } },
  ]
  try {
    await ctx.sessionPersistence.create(meta)
    await ctx.sessionPersistence.append(sessionId, events)
  } finally {
    await ctx.fiber.dispose()
  }
}

describe('parent-only override inheritance snapshot', () => {
  it('confines a delegated child through the assembled headless app', async () => {
    let cwd = ''
    const result = await runLoaderSmoke({
      label: 'subagent inheritance headless stream-json snapshot',
      tempDirPrefix: 'dsh-subagent-inherit-',
      binScript,
      configPath,
      binArgs: ['--config', configPath, '--output-format', 'stream-json', task],
      tsconfigPath,
      env: {
        // The primary fixture path must exist for llm-replay's config guard;
        // the override sidecar fully replaces the derived parent script.
        DSH_SNAPSHOT_FILE: replayOverride,
        DSH_SNAPSHOT_OVERRIDE: replayOverride,
        DSH_SNAPSHOT_CHILD_FILES: childReplay,
      },
      prepare: async (runCwd) => {
        cwd = runCwd
        await seedReadOnlyParent(join(runCwd, '.sessions'), runCwd)
      },
      inspect: async (runCwd) => {
        // THE physical fact: the child's write never reached the disk. Under
        // the deployment default (workspace-write) alone it would succeed.
        await expect(readFile(join(runCwd, 'inherited.txt'), 'utf8')).rejects.toMatchObject({ code: 'ENOENT' })

        // Collect both persisted logs (parent resumed turn + child run).
        const sessionsDir = join(runCwd, '.sessions')
        const files = (await readdir(sessionsDir, { recursive: true })).filter(file => file.endsWith('.jsonl'))
        const logs = await Promise.all(files.map(async file => readFile(join(sessionsDir, file), 'utf8')))
        const headerOf = (content: string): Record<string, unknown> =>
          JSON.parse(content.split('\n')[0] ?? '{}') as Record<string, unknown>
        const parent = logs.find(content => content.includes('"subagent-inheritance-parent"'))
        const child = logs.find(content => typeof headerOf(content).parentSession === 'string')
        if (parent === undefined || child === undefined) throw new Error('missing persisted parent or child log')

        const childRecords = child.trimEnd().split('\n').map(
          line => JSON.parse(line) as Record<string, unknown>,
        )
        expect(childRecords[1]).toMatchObject({
          type: 'sandbox/mode',
          seq: 0,
          data: { mode: 'read-only', source: 'delegation' },
        })

        const context: NormalizeContext = { sessionIds: [sessionId, String(headerOf(child).id)], cwd }
        const normalizedParent = scrubRequestHeaders(normalizeSessionLog(parent, context))
        const normalizedChild = scrubRequestHeaders(normalizeSessionLog(child, context))
        if (refreshing) {
          await writeFile(parentExpected, normalizedParent)
          await writeFile(childExpected, normalizedChild)
        }
        expect(normalizedParent).toBe(await readFile(parentExpected, 'utf8'))
        expect(normalizedChild).toBe(await readFile(childExpected, 'utf8'))
        // The child's real write was denied by the real fence.
        expect(normalizedChild).toContain('file access denied under read-only mode')
      },
    })

    expect(result.stderr).toBe('')
    const records = result.stdout.trimEnd().split('\n').map(line => JSON.parse(line) as Record<string, unknown>)
    expect(records.at(-1)).toMatchObject({
      type: 'result',
      success: true,
      sessionId,
      result: 'The delegated child was denied by the sandbox. PARENT_DONE',
      reason: { kind: 'completed' },
    })
  }, LOADER_SMOKE_TEST_TIMEOUT_MS)
})
