// kilocode_change - new file
//
// Regression test for the freeze bug: before the caps + worker offload,
// Snapshot.diffFull on a file with tens of thousands of lines could block
// the thread for minutes. In the TUI, that same thread hosts the Hono
// server — so the POST /:id/abort endpoint (what ESC fires) never ran.
//
// This test proves:
//   1. A synthetic freeze workload (30k-line file) now completes quickly.
//   2. The abort endpoint responds within a bounded time while the freeze
//      workload runs concurrently.
//   3. A concurrent setInterval keeps ticking — i.e. the event loop keeps
//      breathing and ESC would be delivered.

import { test, expect, afterEach, mock } from "bun:test"
import { $ } from "bun"
import { Effect, Fiber } from "effect"
import { provideTestInstance } from "../fixture/fixture"
import { Server } from "../../src/server/server"
import { Session } from "../../src/session/session"
import { Snapshot } from "../../src/snapshot"
import { Filesystem } from "../../src/util/filesystem"
import * as Log from "@opencode-ai/core/util/log"
import { disposeAllInstances, tmpdir } from "../fixture/fixture"

void Log.init({ print: false })

function run<A>(body: (snapshot: Snapshot.Interface) => Effect.Effect<A, never, Session.Service>) {
  return Effect.runPromise(
    Snapshot.Service.use(body).pipe(Effect.provide(Snapshot.defaultLayer), Effect.provide(Session.defaultLayer)),
  )
}

afterEach(async () => {
  mock.restore()
  await disposeAllInstances()
})

test("pathological diffFull workload finishes quickly and does not block abort", async () => {
  // 3000-line file that churns every line between snapshots. Before the fix
  // this ran through structuredPatch at context=MAX_SAFE_INTEGER synchronously
  // and could take minutes.
  const v1 = Array.from({ length: 3000 }, (_, i) => `v1_line_${i}`).join("\n") + "\n"
  const v2 = Array.from({ length: 3000 }, (_, i) => `v2_line_${i}`).join("\n") + "\n"

  await using tmp = await tmpdir({
    git: true,
    init: async (dir) => {
      await Filesystem.write(`${dir}/fat.json`, v1)
      await $`git add .`.cwd(dir).quiet()
      await $`git commit --no-gpg-sign -m init`.cwd(dir).quiet()
    },
  })

  await provideTestInstance({
    directory: tmp.path,
    fn: () =>
      run((snapshot) =>
        Effect.gen(function* () {
          const sessions = yield* Session.Service
          const session = yield* sessions.create({})

          const before = yield* snapshot.track()
          expect(before).toBeTruthy()

          yield* Effect.promise(() => Filesystem.write(`${tmp.path}/fat.json`, v2))
          const after = yield* snapshot.track()
          expect(after).toBeTruthy()

          // Kick off a diffFull that exercises the freeze path.
          const diff = yield* snapshot.diffFull(before!, after!).pipe(Effect.forkChild({ startImmediately: true }))

          // Concurrently keep a tick counter running. If the event loop blocks we
          // will see this count fall behind wall-clock elapsed.
          let ticks = 0
          const start = Date.now()
          const timer = setInterval(() => {
            ticks++
          }, 25)

          // Fire an abort request against the Hono app in the middle of the diff.
          const app = Server.Default().app
          const abortStart = Date.now()
          const res = yield* Effect.promise(() =>
            Promise.resolve(app.request(`/session/${session.id}/abort`, { method: "POST" })),
          )
          const abortLatency = Date.now() - abortStart
          expect(res.status).toBe(200)
          // The abort endpoint must respond well under a second even under load.
          expect(abortLatency).toBeLessThan(2000)

          const diffs = yield* Fiber.join(diff)
          clearInterval(timer)
          const total = Date.now() - start

          // The freeze workload must finish in bounded time. Five seconds is
          // generous even for a slow CI box; without the fix this hangs.
          expect(total).toBeLessThan(5000)
          // And we must have ticked at least a few times during the work, proving
          // the event loop stayed responsive (ESC would actually arrive).
          expect(ticks).toBeGreaterThan(0)

          // With git-based diff the patch is a real unified diff, not empty.
          const hit = diffs.find((d) => d.file === "fat.json")
          expect(hit).toBeDefined()
          expect(hit!.patch).toMatch(/^diff --git /m)
          expect(hit!.patch).toContain("-v1_line_0")
          expect(hit!.patch).toContain("+v2_line_0")
          expect(hit!.additions).toBeGreaterThan(0)
          expect(hit!.deletions).toBeGreaterThan(0)
        }),
      ),
  })
})
