import { expect } from "e2e";
import { test, WEB, makeState, runPromise } from "./t3.ts";
import { T3_HOME } from "../e2e.config.ts";

test("An optionless saved thread retains catalog effort instead of new-thread effort", WEB, async ({ t3, app, screen, browser }) => {
  const project = await t3.project("composer-demo");
  await t3.thread({ project, title: "Optionless conversation", messages: ["Hello"] });
  const { cli } = makeState(T3_HOME);
  const listed = JSON.parse((await runPromise(cli(["thread", "list", "--project", project, "--json"]))).stdout);
  const threads = Array.isArray(listed) ? listed : listed.threads;
  const threadId = threads[0].threadId ?? threads[0].id;
  await browser.addInitScript(() => {
    const send = WebSocket.prototype.send;
    (window as any).followupSent = new Promise(resolve => {
      WebSocket.prototype.send = function(data) {
        if (typeof data === "string") {
          for (const frame of [JSON.parse(data)].flat()) {
            if (frame.payload?.type === "message.dispatch") resolve(frame.payload.modelSelection);
          }
        }
        return send.call(this, data);
      };
    });
  });
  await t3.open();
  await browser.locator("li[data-thread-item]").first().tap();
  await browser.evaluate(async ({ threadId }) => {
    const socket = new WebSocket(`${location.origin.replace('http', 'ws')}/ws?orchestrationProtocol=2&clientCapabilities=settingsSecretReferences`);
    await new Promise<void>((resolve, reject) => { socket.onopen = () => resolve(); socket.onerror = () => reject(new Error('socket failed')); });
    let count = 0;
    const rpc = (tag: string, payload: unknown) => new Promise<any>((resolve, reject) => {
      const id = String(++count);
      const handler = (event: MessageEvent) => {
        for (const value of String(event.data).split('\n').filter(Boolean).flatMap(line => { const v = JSON.parse(line); return Array.isArray(v) ? v : [v]; })) {
          if (value._tag === 'Ping') socket.send(JSON.stringify({_tag:'Pong'}));
          if (value._tag !== 'Exit' || value.requestId !== id) continue;
          socket.removeEventListener('message', handler);
          if (value.exit._tag === 'Success') resolve(value.exit.value); else reject(new Error(JSON.stringify(value.exit)));
        }
      };
      socket.addEventListener('message', handler);
      socket.send(JSON.stringify({_tag:'Request', id, tag, payload, headers:[]}));
    });
    try {
      await rpc('server.updateSettings', {patch: {
        defaultModelSelection: {instanceId:'claudeAgent', model:'claude-opus-5-5', options:[{id:'effort',value:'high'}]},
      }, providerInstanceMutation: {operation:'upsert', instanceId:'claudeAgent', instance:{driver:'claudeAgent',enabled:true,config:{binaryPath:'/bin/false'}}}});
      await rpc('orchestration.dispatchCommand', {type:'thread.model-selection.set', commandId:crypto.randomUUID(), threadId, modelSelection:{instanceId:'claudeAgent',model:'claude-opus-5-5'}});
    } finally { socket.close(); }
  }, { threadId });
  await browser.locator("li[data-thread-item]").first().tap();
  await screen.getByRole('textbox').fill('Continue with the existing effort');
  await screen.getByRole('button', 'Dismiss Claude provider error').tap();
  await app.screenshot('untouched-composer');
  await screen.getByRole('button', 'Submit message', {exact:true}).tap();
  const submitted = await browser.evaluate(async () => await (window as any).followupSent);
  console.log("Submitted selection", JSON.stringify(submitted));
  await screen.getByRole('textbox').fill('Next follow-up');
  await app.screenshot('after-untouched-send');
  expect(submitted.options?.find((option: {id:string; value:unknown}) => option.id === 'effort')?.value ?? 'medium').toBe('medium');
  await expect(screen.getByRole('button', 'Medium', {exact:false})).toBeVisible();
});
