import { execFileSync } from 'node:child_process';
import { dirname, join } from 'node:path';
import { mkdir, writeFile, readFile } from 'node:fs/promises';
import { expect } from 'e2e';
import * as NetService from '@t3tools/shared/Net';
import * as NodeCrypto from '@effect/platform-node/NodeCrypto';
import * as Effect from 'effect/Effect';
import * as Layer from 'effect/Layer';
import * as Option from 'effect/Option';
import * as EnvironmentAuth from '../../apps/server/src/auth/EnvironmentAuth.ts';
import * as ServerConfig from '../../apps/server/src/config.ts';
import { resolveCliAuthConfig } from '../../apps/server/src/cli/config.ts';
import { test, WEB, makeState, runPromise } from './t3.ts';
import { APP_DIR, T3_HOME } from '../e2e.config.ts';

// Run with forge-fixture.mjs and an isolated gh http_unix_socket configuration.
const control = async (phase: string) => {
  const answer = await fetch('http://127.0.0.1:19128/control', {method:'POST',body:JSON.stringify({phase})});
  if (!answer.ok) throw new Error('Synthetic forge control unavailable');
};

test('PR watch wakes again when remaining checks finish after an early failure', WEB, async ({t3,app,screen,browser}) => {
  await control('running');
  const project = await t3.project('watch-demo');
  await t3.thread({project,title:'Watch CI completion',messages:['Watch this PR until all checks finish.'],model:'multi-turn'});
  const {cli} = makeState(T3_HOME);
  const command = async (args: string[]) => (await runPromise(cli(args))).stdout;
  const threads = JSON.parse(await command(['thread','list','--project',project,'--json']));
  const threadId = threads[0].threadId;
  const config = await runPromise(resolveCliAuthConfig({baseDir:Option.some(T3_HOME)},Option.none(),{quietLogs:true}).pipe(Effect.provide(NetService.layer)));
  const issued = await runPromise(Effect.gen(function*(){
    const auth = yield* EnvironmentAuth.EnvironmentAuth;
    return yield* auth.issueMcpClientSession({label:'Proof driver',access:'full-access',peerLink:false,client:{deviceType:'bot'}});
  }).pipe(Effect.provide(EnvironmentAuth.layerRuntime.pipe(Layer.provide(ServerConfig.layer(config)))),Effect.provide(NodeCrypto.layer),Effect.scoped));
  const token = issued.token;
  const {origin} = JSON.parse(await readFile(`${T3_HOME}/userdata/server-runtime.json`,'utf8'));
  let session: string | null = null;
  let id=0;
  const rpc=async(method:string,params:unknown)=>{
    const response=await fetch(`${origin}/mcp`,{method:'POST',headers:{authorization:`Bearer ${token}`,'content-type':'application/json',accept:'application/json, text/event-stream',...(session?{'mcp-session-id':session}:{})},body:JSON.stringify({jsonrpc:'2.0',id:++id,method,params})});
    session=response.headers.get('mcp-session-id')??session;
    const raw=await response.text();
    if(!response.ok)throw new Error(`MCP ${method}: ${response.status} ${raw}`);
    const data=raw.startsWith('event:')||raw.startsWith('data:')?JSON.parse(raw.split('\n').find(line=>line.startsWith('data:'))!.slice(5)):JSON.parse(raw);
    if(data.error||data.result?.isError)throw new Error(JSON.stringify(data));
    return data.result;
  };
  await rpc('initialize',{protocolVersion:'2025-06-18',capabilities:{},clientInfo:{name:'proof-driver',version:'1'}});
  await rpc('tools/call',{name:'watch_pull_request',arguments:{threadId,url:'https://github.com/scenario/watch-demo/pull/42'}});
  const environmentId = (await readFile(`${T3_HOME}/userdata/environment-id`,'utf8')).trim();
  await t3.open();
  await screen.getByRole('combobox','Search threads').waitFor({state:'visible'});
  await app.open(`/${environmentId}/${threadId}`);
  await control('failed');
  const failure=screen.getByText('#42: checks failed',{exact:false}).first();
  await failure.waitFor({state:'visible',timeout:155_000});
  await screen.getByRole('button','Stop generation').waitFor({state:'hidden',timeout:30_000});
  await app.screenshot('early-failure');
  await control('finished');
  const finished=screen.getByText('#42: checks finished',{exact:true});
  // The same observation window on each commit includes the next normal two-minute sweep.
  try { await finished.waitFor({state:'visible',timeout:150_000}); } catch {}
  await screen.getByRole('button','Stop generation').waitFor({state:'hidden',timeout:30_000});
  await app.screenshot('remaining-checks-finished');
  const sha = execFileSync('git',['rev-parse','HEAD'],{cwd:APP_DIR,encoding:'utf8'}).trim();
  const output = join(dirname(process.env.GH_CONFIG_DIR!),`runtime-${sha.slice(0,10)}-${Date.now()}`);
  await mkdir(output,{recursive:true});
  const wakes = JSON.parse(execFileSync('sqlite3',['-readonly','-json',`${T3_HOME}/userdata/statev2.sqlite`,
    "SELECT created_at AS time, json_extract(payload_json, '$.notification.summary') AS summary, json_extract(payload_json, '$.text') AS text FROM orchestration_v2_projection_messages WHERE json_extract(payload_json, '$.notification') IS NOT NULL ORDER BY created_at"],{encoding:'utf8'}));
  await writeFile(join(output,'wake-transcript.json'),JSON.stringify({sha,wakes},null,2));
  await expect(finished).toHaveCount(1);
  await expect(finished).toBeVisible();
});
