import { writeFile } from "node:fs/promises";
import { expect } from "e2e";
import * as HostProcess from "@t3tools/shared/HostProcess";
import * as Effect from "effect/Effect";
import { APP_DIR, T3_HOME } from "../e2e.config.ts";
import { run } from "./shell.ts";
import { runPromise, test, WEB } from "./t3.ts";

// Use the same owner-authorized MCP session as the app's thread CLI.
const issueToken = async () => {
  const helper = `${APP_DIR}/apps/server/scheduled-proof-token.mjs`;
  await writeFile(helper, `
    import * as Effect from 'effect/Effect';
    import * as Layer from 'effect/Layer';
    import * as Option from 'effect/Option';
    import * as NodeServices from '@effect/platform-node/NodeServices';
    import * as NodeCrypto from '@effect/platform-node/NodeCrypto';
    import * as Net from '@t3tools/shared/Net';
    import * as Auth from './src/auth/EnvironmentAuth.ts';
    import * as Config from './src/config.ts';
    import { resolveCliAuthConfig } from './src/cli/config.ts';
    const program = Effect.gen(function* () {
      const config = yield* resolveCliAuthConfig({baseDir: Option.some(${JSON.stringify(T3_HOME)})}, Option.none(), {quietLogs:true});
      return yield* Effect.gen(function* () {
        const auth = yield* Auth.EnvironmentAuth;
        return yield* auth.issueMcpClientSession({label:'Scheduled task proof',access:'full-access',peerLink:false,client:{deviceType:'bot'}});
      }).pipe(Effect.provide(Auth.layerRuntime.pipe(Layer.provide(Config.layer(config)))));
    });
    const session = await Effect.runPromise(program.pipe(Effect.provide(Layer.mergeAll(NodeServices.layer, NodeCrypto.layer, Net.layer))));
    console.log(session.token);
  `);
  return runPromise(Effect.gen(function* () {
    const node = yield* HostProcess.ExecutablePath;
    const result = yield* run(node, [helper], {cwd: `${APP_DIR}/apps/server`});
    return result.stdout.trim();
  }));
};

test("A failed manual scheduled run returns its recorded dispatch error", WEB, async ({t3, app, screen}) => {
  await t3.project("Demo", {"README.md":"Synthetic scheduled task demonstration.\n"});
  const token = await issueToken();
  let sessionId: string | null = null;
  let id = 0;
  const send = async (message: object) => {
    const response = await fetch(`${app.baseUrl}/mcp`, {
      method:"POST",
      headers:{accept:"application/json, text/event-stream", "content-type":"application/json", authorization:`Bearer ${token}`, ...(sessionId ? {"mcp-session-id":sessionId} : {})},
      body:JSON.stringify({jsonrpc:"2.0",...message}),
    });
    sessionId = response.headers.get("mcp-session-id") ?? sessionId;
    const body = await response.text();
    if (!response.ok) throw new Error(`MCP ${response.status}: ${body}`);
    if (!body) return null;
    return JSON.parse(body.startsWith("event:") || body.startsWith("data:") ? body.split("\n").find(line=>line.startsWith("data: "))!.slice(6) : body);
  };
  await send({id:++id,method:"initialize",params:{protocolVersion:"2025-06-18",capabilities:{},clientInfo:{name:"scheduled-task-proof",version:"1"}}});
  await send({method:"notifications/initialized"});
  const call = async (name: string, args: object) => {
    const reply = await send({id:++id,method:"tools/call",params:{name,arguments:args}});
    if (reply.error || reply.result?.isError) throw new Error(JSON.stringify(reply));
    return reply.result.structuredContent ?? JSON.parse(reply.result.content.find((c:any)=>c.type === "text").text);
  };
  const projects = await call("t3_project_list", {});
  const projectId = projects.projects.find((p:any)=>p.title === "Demo").id;
  await call("t3_project_update", {projectId,defaultModelSelection:{instanceId:"claudeAgent",model:"claude-haiku-5-5"}});
  await call("schedule_task", {projectId,title:"Claude interval task",prompt:"Report the repository status.",schedule:{type:"interval",everyMs:900000},enabled:false,bindToCurrentThread:false});
  const listed = await call("list_scheduled_tasks", {projectId});
  const taskId = listed.tasks[0].scheduledTaskId;
  await t3.open("/settings/scheduled-tasks");
  const result = await call("run_scheduled_task_now", {taskId});
  await screen.getByText(`Provider instance "claudeAgent" is disabled for project ${projectId}`, {exact:false}).waitFor({state:"visible"});
  await app.screenshot("recorded-dispatch-error");
  const project = projects.projects.find((p:any)=>p.id === projectId).workspaceRoot;
  const thread = await t3.thread({project,title:"Live scheduled-task MCP response",messages:[
    "Live MCP probe (full-access client)\n\nClaude interval task: disabled schedule, new thread each run, project checkout, Claude Haiku 5.5, every 15 minutes. Claude provider disabled.\n\nrun_scheduled_task_now returned this raw structuredContent:\n\n" + JSON.stringify(result,null,2).split("\n").join("  \n")
  ]});
  await t3.open(thread);
  await screen.getByText("Live MCP probe (full-access client)", {exact:false}).waitFor({state:"visible"});
  await screen.getByText("Live MCP probe (full-access client)", {exact:false}).tap();
  await screen.getByText("run_scheduled_task_now returned this raw structuredContent:", {exact:false}).waitFor({state:"visible"});
  await app.screenshot("manual-run-mcp-response");
  await expect(result).toEqual({taskId,threadId:null,lastRunStatus:"failed",lastRunError:`Provider instance "claudeAgent" is disabled for project ${projectId}.`,runCount:1,nextRunAt:null});
});
