import { spawn, spawnSync } from "child_process"
import fs from "fs"
import path from "path"
import { tool } from "@opencode-ai/plugin"
import { CATEGORIES, resolveMemoryDir } from "./_memory-shared"

const HOOK_BODY = "#!/bin/bash\ngit push origin master 2>/dev/null || true\n"

function run(cmd: string, args: string[], opts?: { cwd?: string }) {
  return spawnSync(cmd, args, { encoding: "utf-8", ...opts })
}

function isEmptyDir(dir: string): boolean {
  if (!fs.existsSync(dir)) return true
  return fs.readdirSync(dir).filter((n) => !n.startsWith(".")).length === 0
}

function autoSetup(memoryDir: string, remote: string | undefined): string[] {
  const steps: string[] = []
  if (!fs.existsSync(memoryDir)) {
    fs.mkdirSync(memoryDir, { recursive: true })
    steps.push(`created directory: ${memoryDir}`)
  }
  if (!fs.existsSync(path.join(memoryDir, ".git"))) {
    if (remote && isEmptyDir(memoryDir)) {
      const r = run("git", ["clone", "--origin", "origin", remote, memoryDir])
      if (r.status !== 0) {
        run("git", ["init", memoryDir])
        steps.push(`git init (clone failed: ${(r.stderr || "").trim()})`)
      } else {
        const co = run("git", ["-C", memoryDir, "checkout", "master"])
        if (co.status !== 0) {
          process.stderr.write(
            `[memory-save] git checkout master failed (exit ${co.status}): ${(co.stderr || co.stdout || "").trim()}\n`
          )
          steps.push("cloned remote (checkout master failed — check stderr)")
        } else {
          steps.push(`cloned remote: ${remote}`)
        }
      }
    } else {
      run("git", ["init", memoryDir])
      steps.push("git init")
    }
  }
  if (remote) {
    const hookPath = path.join(memoryDir, ".git", "hooks", "post-commit")
    let needHook = true
    if (fs.existsSync(hookPath)) {
      if (fs.readFileSync(hookPath, "utf-8") === HOOK_BODY) needHook = false
    }
    if (needHook) {
      fs.mkdirSync(path.dirname(hookPath), { recursive: true })
      fs.writeFileSync(hookPath, HOOK_BODY, { mode: 0o755 })
      steps.push("installed post-commit hook (auto-push)")
    }
    const cur = run("git", ["-C", memoryDir, "remote", "get-url", "origin"])
    if (cur.status !== 0 || (cur.stdout || "").trim() !== remote) {
      run("git", ["-C", memoryDir, "remote", "set-url", "origin", remote])
      steps.push(`set remote origin: ${remote}`)
    }
  }
  for (const cat of CATEGORIES) {
    const catDir = path.join(memoryDir, cat)
    if (!fs.existsSync(catDir)) fs.mkdirSync(catDir, { recursive: true })
  }
  return steps
}

export default tool({
  description:
    "Commit and re-index all pending memory changes. Call AFTER using Write/Edit tools on ~/.local/share/opencode/opencode-memory/{category}/{filename}.md. " +
    "Handles: auto-setup (mkdir + git init/clone + hook) on first call, git add -A, commit (message derived from changed files), async RAG re-index (fire-and-forget, log to .rag/reindex.log). " +
    "WHEN TO SAVE (always save when you discover something reusable): learned non-obvious API pattern/tool quirk/workaround, discovered repo structure/conventions/gotchas, resolved tricky debugging with non-obvious root cause, learned team/ownership info. " +
    "DO NOT save: one-off answers, things in public docs, context only relevant to the current task. " +
    "CATEGORIES: preferences, repos, technical, people, workflows, snippets, notes. " +
    "FRONTMATTER FORMAT: ---\\ntitle: ...\\ntags: [...]\\nsummary: ...\\ncreated: YYYY-MM-DD\\nupdated: YYYY-MM-DD\\nimportance: high|medium|low\\nrelated: [...]\\n---",
  args: {},
  async execute(_args, context) {
    const memoryDir = resolveMemoryDir()
    const remote = process.env.OPENCODE_MEMORY_REMOTE
    const setupSteps = autoSetup(memoryDir, remote)

    const addR = run("git", ["-C", memoryDir, "add", "-A"])
    if (addR.status !== 0) {
      return `⚠️ memory-save: git add failed (exit ${addR.status}): ${addR.stderr || addR.stdout}`
    }
    const diffR = run("git", ["-C", memoryDir, "diff", "--name-only", "--cached"])
    if (diffR.status !== 0) {
      return `⚠️ memory-save: git diff failed (exit ${diffR.status}): ${diffR.stderr || diffR.stdout}`
    }
    const changed = (diffR.stdout || "").trim().split("\n").filter(Boolean)
    if (changed.length === 0) return "No changes to sync"

    const msg = `memory: sync ${changed.join(", ")}`
    const commitR = run("git", ["-C", memoryDir, "commit", "-m", msg])
    if (commitR.status !== 0) {
      return `⚠️ memory-save: git commit failed (exit ${commitR.status}): ${commitR.stderr || commitR.stdout}`
    }

    if (process.env.OPENAI_BASE_URL) {
      const indexDir = path.join(memoryDir, ".rag")
      fs.mkdirSync(indexDir, { recursive: true })
      const child = spawn("python3", ["-m", "src.memory", "index", memoryDir, "-o", indexDir], {
        cwd: context.worktree,
        stdio: "ignore",
        detached: true,
      })
      child.unref()
    }

    const parts = [`Synced: ${changed.join(", ")}`]
    if (setupSteps.length > 0) parts.push(`Auto-setup: ${setupSteps.join("; ")}`)
    if (!process.env.OPENAI_BASE_URL) {
      parts.push("(semantic index skipped: OPENAI_BASE_URL not set — keyword search still works)")
    }
    return parts.join("\n")
  },
})