import { execSync } from "node:child_process";
import { mkdirSync, rmSync } from "node:fs";
import path from "node:path";

import { defineAgent, defineWorkflow } from "@flue/runtime";
import type { WorkflowRouteHandler } from "@flue/runtime";
import { local } from "@flue/runtime/node";
import { Effect } from "effect";
import * as v from "valibot";

import { Gitea } from "../git/gitea-service";
import type { GiteaIssue } from "../git/gitea-service";
import { env, runWithGitea } from "../tools/runtime";

const shellQuote = (value: string): string =>
  `'${value.replaceAll("'", `'"'"'`)}'`;

const runHost = (command: string, options?: { cwd?: string }): string =>
  execSync(command, {
    encoding: "utf-8",
    timeout: 120_000,
    ...(options?.cwd ? { cwd: options.cwd } : {}),
  }).trim();

const createWorktree = (
  issueNumber: number
): { branch: string; path: string } => {
  const branch = `zopu/issue-${issueNumber}-${Date.now()}`;
  const worktreePath = path.join(env.WORKTREE_ROOT, `issue-${issueNumber}`);
  mkdirSync(env.WORKTREE_ROOT, { recursive: true });
  rmSync(worktreePath, { force: true, recursive: true });
  runHost(
    `git worktree add -b ${shellQuote(branch)} ${shellQuote(worktreePath)} ${shellQuote(env.GIT_DEFAULT_BRANCH)}`
  );
  return { branch, path: worktreePath };
};

const publishChanges = async (
  worktreePath: string,
  branch: string,
  issueNumber: number,
  issueTitle: string
): Promise<{
  base: string;
  head: string;
  number: number;
  state: string;
  status: string;
  url: string;
}> => {
  const status = runHost("git status --porcelain=v1", { cwd: worktreePath });
  if (!status) {
    return {
      base: "",
      head: branch,
      number: 0,
      state: "",
      status: "no_changes",
      url: "",
    };
  }

  const commitMessage = `Resolve #${issueNumber}: ${issueTitle}`;
  runHost(`git add --all && git commit -m ${shellQuote(commitMessage)}`, {
    cwd: worktreePath,
  });
  runHost(
    `git push ${shellQuote(env.GIT_SSH_REMOTE)} HEAD:${shellQuote(branch)}`,
    { cwd: worktreePath }
  );

  const pr = await runWithGitea(
    Effect.gen(function* pr() {
      const gitea = yield* Gitea;
      return yield* gitea.createPullRequest({
        base: env.GIT_DEFAULT_BRANCH,
        body: `Resolves #${issueNumber}.\n\nGenerated by the Zopu work actor using GLM 5.2.`,
        head: branch,
        title: `Resolve #${issueNumber}: ${issueTitle}`,
      });
    })
  );
  return {
    base: pr.base,
    head: pr.head,
    number: pr.number,
    state: pr.state,
    status: "pull_request_open",
    url: pr.htmlUrl,
  };
};

const cleanupWorktree = (worktreePath: string): void => {
  try {
    runHost(`git worktree remove ${shellQuote(worktreePath)} --force`);
  } catch {
    // Best-effort cleanup.
  }
};

const workActor = defineAgent(() => ({
  cwd: "/workspace/repository",
  instructions: `You are the Zopu work actor operating inside an isolated AgentOS VM with a git worktree at /workspace/repository.

Your job:
1. Read the issue you received.
2. Inspect the codebase to find the relevant files.
3. Implement the fix or feature directly.
4. Verify your changes if tests or a typechecker are available.

Do NOT run git commit, push, or create pull requests. The host-side workflow handles publishing after you finish. Focus on correct code changes only.`,
  model: `${env.AGENT_MODEL_PROVIDER}/${env.AGENT_MODEL_NAME}`,
  sandbox: local({ cwd: "/workspace/repository" }),
}));

export default defineWorkflow({
  agent: workActor,
  input: v.object({
    issueNumber: v.pipe(v.number(), v.integer()),
  }),
  async run({ harness, input }) {
    const issue: GiteaIssue = await runWithGitea(
      Effect.gen(function* issue() {
        const gitea = yield* Gitea;
        return yield* gitea.getIssue(input.issueNumber);
      })
    );

    const { branch, path: worktreePath } = createWorktree(input.issueNumber);

    const session = await harness.session();
    await session.prompt(
      `Resolve issue #${issue.number}: ${issue.title}\n\n${issue.body}\n\nInspect /workspace/repository and implement the fix.`
    );

    const result = await publishChanges(
      worktreePath,
      branch,
      input.issueNumber,
      issue.title
    );
    cleanupWorktree(worktreePath);

    return {
      branch,
      issueNumber: input.issueNumber,
      pullRequest: {
        base: result.base,
        head: result.head,
        number: result.number,
        state: result.state,
        url: result.url,
      },
      status: result.status,
    };
  },
});

export const route: WorkflowRouteHandler = (_c, next) => next();
