import * as FS from "node:fs";
import * as CP from "node:child_process";
import * as NodeServices from "@effect/platform-node/NodeServices";
import * as NodeWS from "@effect/platform-node/NodeWS";
import * as Effect from "effect/Effect";
import * as Layer from "effect/Layer";
import * as Stream from "effect/Stream";
import * as DateTime from "effect/DateTime";
import { RpcClient, RpcSerialization } from "effect/rpc";
import { Socket } from "effect/socket";
import { DEFAULT_SERVER_SETTINGS, WsRpcGroup, ORCHESTRATION_PROTOCOL_VERSION, ORCHESTRATION_PROTOCOL_HEADER } from "@t3tools/contracts";
import * as ProjectionStore from "./src/orchestration-v2/ProjectionStore.ts";
import * as ProjectStore from "./src/orchestration-v2/ProjectStore.ts";
import * as SqlitePersistence from "./src/persistence/Sqlite.ts";
import { limitRecoveryCommand } from "./src/orchestration-v2/UsageLimitRecoveryWorker.ts";
const [mode, dir, port] = process.argv.slice(2);
const DAY_MS = 86400000;
const origin = `http://127.0.0.1:${port}`;
const worktree = `${dir}/worktrees/demo/feature`;
const repo = `${dir}/demo`;
const log = (label, data) => console.log(label + " " + JSON.stringify(data));
const git = (args) => CP.execFileSync("git", args, {cwd:repo,stdio:"pipe"});
if (mode === "seed") {
 FS.mkdirSync(repo,{recursive:true});
 git(["init","-b","main"]); git(["config","user.name","Demo"]); git(["config","user.email","demo@example.invalid"]);
 FS.writeFileSync(`${repo}/README.md`,"Synthetic cleanup demonstration\n");
 git(["add","."]); git(["commit","-m","Initial synthetic project"]);
 FS.mkdirSync(`${dir}/worktrees/demo`,{recursive:true});
 git(["worktree","add","-b","feature",worktree]);
 FS.mkdirSync(`${dir}/userdata`,{recursive:true});
 FS.writeFileSync(`${dir}/userdata/settings.json`, JSON.stringify({autoResumeLimitedThreads:true,snoozeLimitedThreads:true,sidebarAutoSettleAfterDays:null,worktreesDirectory:`${dir}/worktrees`,storageCleanup:{worktreeAfterDays:null}}));
 const program = Effect.gen(function*(){
  const store = yield* ProjectionStore.ProjectionStoreV2;
  const projects = yield* ProjectStore.ProjectStoreV2;
  const now = yield* DateTime.now; const nowMs = DateTime.toEpochMillis(now);
  const failedAt = DateTime.makeUnsafe(nowMs - 6 * DAY_MS);
  const modelSelection = {instanceId:"demo",model:"demo"}; const driver = "codex";
  const label = "STALE", threadId=label,runId=`${label}:run`,nodeId=`${label}:root`;
  const resetAt = DateTime.formatIso(DateTime.makeUnsafe(nowMs - 5 * DAY_MS));
  const fixture = {worktree};
  yield* projects.apply({type:"project.created",payload:{projectId:"project-1",title:"Cleanup demonstration",workspaceRoot:repo,defaultModelSelection:modelSelection,scripts:[],createdAt:DateTime.formatIso(failedAt),updatedAt:DateTime.formatIso(failedAt)}});
      yield* store.apply({
        id: `${label}:thread`, type: "thread.created", threadId, occurredAt: failedAt,
        payload: {
          id: threadId, projectId: "project-1", title: label,
          providerInstanceId: modelSelection.instanceId, modelSelection,
          runtimeMode: "full-access", interactionMode: "default", branch: "feature",
          worktreePath: fixture.worktree, activeProviderThreadId: null,
          lineage: { parentThreadId: null, relationshipToParent: null, rootThreadId: threadId },
          forkedFrom: null, createdBy: "user", creationSource: "web",
          createdAt: failedAt, updatedAt: failedAt, archivedAt: null,
          settledOverride: null, settledAt: null, lastVisitedAt: null, deletedAt: null,
        },
      });
      yield* store.apply({
        id: `${label}:run`, type: "run.created", threadId, runId, nodeId,
        driver, providerInstanceId: modelSelection.instanceId, occurredAt: failedAt,
        payload: {
          id: runId, threadId, ordinal: 1, providerInstanceId: modelSelection.instanceId,
          modelSelection, providerThreadId: null, userMessageId: `${label}:message`,
          rootNodeId: nodeId, activeAttemptId: null, status: "failed",
          requestedAt: failedAt, startedAt: failedAt, completedAt: failedAt,
          checkpointId: null, contextHandoffId: null,
        },
      });
      yield* store.apply({
        id: `${label}:error`, type: "turn-item.updated", threadId, occurredAt: failedAt,
        payload: {
          id: `${label}:error`, threadId, runId, nodeId,
          providerThreadId: null, providerTurnId: null, nativeItemRef: null,
          parentItemId: null, ordinal: 1, status: "failed", title: "Usage limit reached",
          startedAt: failedAt, completedAt: failedAt, updatedAt: failedAt, type: "error",
          failure: { class: "usage_limit", message: "Plan limit reached.",
            resetAt, code: "usageLimitExceeded", retryable: null },
        },
      });

  const thread = (yield* store.getShellSnapshot()).threads.find(t=>t.id===threadId);
  const candidate = (yield* store.getLimitRecoveryCandidates({now,autoResume:true,snooze:true})).find(t=>t.id===threadId);
  log("SEEDED",{failedDaysAgo:6,resetOverdueDays:5,armed:thread.limitRecovery??null,workerCommand:limitRecoveryCommand(candidate,true,nowMs,true)?.type??null,clean:git(["-C",worktree,"status","--porcelain"]).toString()==="",worktreeExists:FS.existsSync(worktree)});
 });
 await Effect.runPromise(program.pipe(Effect.provide(Layer.mergeAll(ProjectionStore.layer,ProjectStore.layer).pipe(Layer.provide(SqlitePersistence.layerFromPath(`${dir}/userdata/statev2.sqlite`)),Layer.provide(NodeServices.layer))),Effect.scoped));
} else {
 const token=CP.execFileSync(process.execPath,["src/bin.ts","auth","session","issue","--base-dir",dir,"--token-only"],{encoding:"utf8",env:{...process.env,T3CODE_DEV_SEED:"0"}}).trim().split("\n").at(-1);
 const headers={Authorization:`Bearer ${token}`,[ORCHESTRATION_PROTOCOL_HEADER]:String(ORCHESTRATION_PROTOCOL_VERSION)};
 const ticket=await (await fetch(`${origin}/api/auth/websocket-ticket`,{method:"POST",headers})).json();
 const protocol = RpcClient.layerProtocolSocket().pipe(Layer.provide(Socket.layerWebSocket(`${origin.replace("http","ws")}/ws?orchestrationProtocol=${ORCHESTRATION_PROTOCOL_VERSION}&wsTicket=${encodeURIComponent(ticket.ticket)}`)),Layer.provide(RpcSerialization.layerJson),Layer.provide(NodeWS.layerWebSocketConstructor));
 await Effect.runPromise(Effect.gen(function*(){
  const client=yield* RpcClient.make(WsRpcGroup);
  const snapshot = yield* Effect.promise(()=>fetch(`${origin}/api/orchestration/shell`,{headers}).then(r=>r.json()));
  const thread=snapshot.threads.find(t=>t.id==="STALE");
  log("SERVER_STATE",{status:thread.status,lastErrorClass:thread.lastErrorClass,armed:thread.limitRecovery??null,worktreeExists:FS.existsSync(worktree)});
  yield* client["server.getStorageCleanupReport"]({}).pipe(Stream.runForEach(report=>Effect.sync(()=>{
   const entry=report?.entries.find(e=>e.path===worktree);
   if(entry) log("CLEANUP_REPORT",{trigger:report.trigger,cleanup:entry.outcome,reason:entry.reason,worktreeExists:FS.existsSync(worktree)});
  })),Effect.forkChild);
  yield* client["server.updateSettings"]({patch:{storageCleanup:{worktreeAfterDays:1}}});
  const report=yield* client["server.runStorageCleanup"]({});
  const entry=report.entries.find(e=>e.path===worktree);
  log("CLEANUP_RPC",{cleanup:entry?.outcome??"already removed by settings-triggered sweep",reason:entry?.reason??null,worktreeExists:FS.existsSync(worktree)});
  if(FS.existsSync(worktree)) {
   log("CONTROL",{action:"Turn automatic usage-limit resume off, then repeat cleanup"});
   yield* client["server.updateSettings"]({patch:{autoResumeLimitedThreads:false}});
   const control=yield* client["server.runStorageCleanup"]({});
   const e=control.entries.find(e=>e.path===worktree);
   log("RECOVERY_OFF_CONTROL",{cleanup:e?.outcome,reason:e?.reason,worktreeExists:FS.existsSync(worktree)});
  }
 }).pipe(Effect.provide(protocol),Effect.scoped));
}
