import { execFileSync } from 'node:child_process';
import { expect } from 'e2e';
import { createHash } from 'node:crypto';
import { readFile } from 'node:fs/promises';
import { test, WEB } from './t3.ts';
import { T3_HOME } from '../e2e.config.ts';

test('A queue-only removal wakes a watched PR once while head checks stay green', WEB, async ({t3,app,screen,browser}) => {
 await fetch('http://127.0.0.1:19441/reset');
 const project=await t3.project('Queue watch');
 execFileSync('git',['remote','add','origin','https://github.com/example/queue-demo.git'],{cwd:project});
 await t3.thread({project,title:'Watch merge queue removal',messages:['Watch this pull request through its merge queue.']});
 const runtime=JSON.parse(await readFile(T3_HOME+'/userdata/server-runtime.json','utf8'));
 const pairing=await readFile(T3_HOME+'/pairing-path','utf8');
 const u=new URL(pairing,'http://example.invalid');
 const secret=new URLSearchParams(u.hash.slice(1)).get('token') ?? u.searchParams.get('token');
 if(!secret) throw new Error('Pairing token unavailable; keys '+u.hash.replace(/=.*/g,'=REDACTED'));
 const tokenResponse=await fetch(runtime.origin+'/oauth/token',{method:'POST',headers:{'content-type':'application/x-www-form-urlencoded'},body:new URLSearchParams({requested_token_type:'urn:ietf:params:oauth:token-type:access_token',grant_type:'urn:ietf:params:oauth:grant-type:token-exchange',subject_token_type:'urn:t3:params:oauth:token-type:environment-bootstrap',subject_token:secret})});
 const token=await tokenResponse.json();
 if(!tokenResponse.ok)throw new Error('Token exchange '+JSON.stringify(token));
 async function post(path:string, body:unknown){const r=await fetch(runtime.origin+path,{method:'POST',headers:{'content-type':'application/json',authorization:'Bearer '+token.access_token},body:JSON.stringify(body)});const v=await r.json();if(!r.ok)throw new Error(path+' '+JSON.stringify(v));return v;}
 const registered=await post('/oauth/mcp/register',{client_name:'Queue watch proof',redirect_uris:['http://localhost:19442/callback'],grant_types:['authorization_code'],response_types:['code'],token_endpoint_auth_method:'none'});
 const verifier='queue-watch-proof-verifier-abcdefghijklmnopqrstuvwxyz0123456789';
 const authorization={response_type:'code',client_id:registered.client_id,redirect_uri:'http://localhost:19442/callback',code_challenge:createHash('sha256').update(verifier).digest('base64url'),code_challenge_method:'S256',resource:runtime.origin+'/mcp',state:'proof'};
 const approval=await post('/oauth/mcp/approval',authorization);

 const approved=await post('/oauth/mcp/decision',{authorization,decision:{_tag:'browser-session',access:'full-access',csrfToken:approval.csrfToken??''}});
 const mcpTokenResponse=await fetch(runtime.origin+'/oauth/mcp/token',{method:'POST',headers:{'content-type':'application/x-www-form-urlencoded'},body:new URLSearchParams({grant_type:'authorization_code',code:new URL(approved.redirectTo).searchParams.get('code')!,redirect_uri:authorization.redirect_uri,client_id:registered.client_id,code_verifier:verifier,resource:authorization.resource})});
 const mcpToken=await mcpTokenResponse.json();if(!mcpTokenResponse.ok)throw new Error(JSON.stringify(mcpToken));
 let session='';let id=0;
 async function mcp(method:string,params:unknown){const r=await fetch(runtime.origin+'/mcp',{method:'POST',headers:{'content-type':'application/json',accept:'application/json, text/event-stream',authorization:'Bearer '+mcpToken.access_token,...(session?{'mcp-session-id':session}:{})},body:JSON.stringify({jsonrpc:'2.0',id:++id,method,params})});session=r.headers.get('mcp-session-id')??session;const s=await r.text();const result=JSON.parse(s.startsWith('event:')||s.startsWith('data:')?s.split('\n').find(l=>l.startsWith('data:'))!.slice(5):s);if(result.error)throw new Error(JSON.stringify(result.error));return result.result;}
 await mcp('initialize',{protocolVersion:'2025-03-26',capabilities:{},clientInfo:{name:'queue-proof',version:'1'}});
 const list=await mcp('tools/call',{name:'t3_thread_list',arguments:{allProjects:true}});

 const threadId=list.structuredContent.threads[0].threadId;
 const watch=await mcp('tools/call',{name:'watch_pull_request',arguments:{threadId,url:'https://github.com/example/queue-demo/pull/7'}});
 if(watch.isError)throw new Error(JSON.stringify(watch));
 await t3.open();
 await screen.getByRole('button','Toggle main sidebar').tap();
 await screen.getByRole('button','Toggle main sidebar').tap();
 const path=new URL(list.structuredContent.threads[0].link).pathname.replace('/threads','');
 await app.open(path);
 await screen.getByRole('textbox','Message Watch merge queue removal').tap();
 await fetch('http://127.0.0.1:19441/wait?phase=pending&kind=core', {signal:AbortSignal.timeout(180000)});
 await fetch('http://127.0.0.1:19441/green');
 await screen.getByText('#7: checks passed', {exact:true}).tap({timeout:180000});
 await mcp('tools/call',{name:'t3_thread_wait',arguments:{threadId,timeoutMs:20000}});
 await app.screenshot('head-checks-green');
 await fetch('http://127.0.0.1:19441/remove');
 await fetch('http://127.0.0.1:19441/wait?phase=removed&kind=fingerprint', {signal:AbortSignal.timeout(180000)});
 await new Promise(resolve=>setTimeout(resolve,3000));
 await app.screenshot('queue-removal');
 await expect(screen.getByText('#7: removed from merge queue', {exact:true})).toBeVisible();
 await fetch('http://127.0.0.1:19441/wait?phase=removed&kind=fingerprint&n=2', {signal:AbortSignal.timeout(180000)});
 await new Promise(resolve=>setTimeout(resolve,3000));
 await expect(screen.getByText('#7: removed from merge queue', {exact:true})).toHaveCount(1);
 const messages=execFileSync('sqlite3',['-readonly',T3_HOME+'/userdata/statev2.sqlite',"select payload_json from orchestration_v2_projection_messages where role='user'"],{encoding:'utf8'}).trim().split('\n').map(line=>JSON.parse(line));
 const wakes=messages.filter((item:any)=>item.text?.includes('removed from the merge queue without merging: manually removed'));
 await expect(wakes.length).toBe(1);
 const status=await mcp('tools/call',{name:'t3_thread_list',arguments:{allProjects:true}});
 await expect(status.structuredContent.threads[0].pullRequests[0].watching).toBe(true);
 await app.screenshot('unchanged-sweep');
});
