import { describeRoute, resolver, validator } from "hono-openapi"
import { Hono } from "hono"
import type { UpgradeWebSocket } from "hono/ws"
import { Effect } from "effect"
import z from "zod"
import { Format } from "@/format"
import { TuiRoutes } from "./tui"
import { Instance } from "@/project/instance"
import { InstanceStore } from "@/project/instance-store"
import { Vcs } from "@/project/vcs"
import { Agent } from "@/agent/agent"
import { Skill } from "@/skill"
import { Global } from "@saeeol/core/global"
import { LSP } from "@/lsp/lsp"
import { Command } from "@/command"
import { QuestionRoutes } from "./question"
import { PermissionRoutes } from "./permission"
import { ProjectRoutes } from "./project"
import { SessionRoutes } from "./session"
import { PtyRoutes } from "./pty"
import { McpRoutes } from "./mcp"
import { FileRoutes } from "./file"
import { ConfigRoutes } from "./config"
import { ExperimentalRoutes } from "./experimental"
import { ProviderRoutes } from "./provider"
import { EventRoutes } from "./event"
import { SyncRoutes } from "./sync"
import { InstanceMiddleware } from "./middleware"
import { jsonRequest } from "./trace"
import { register as registerSaeeolRoutes } from "@/saeeol/server/instance"
import * as LTM from "@/ltm"
import * as Embedder from "@/provider/local/embedder"

export const InstanceRoutes = (upgrade: UpgradeWebSocket): Hono => {
  const app = new Hono()

  const full = app 
  full
    .route("/project", ProjectRoutes())
    .route("/pty", PtyRoutes(upgrade))
    .route("/config", ConfigRoutes())
    .route("/experimental", ExperimentalRoutes())
    .route("/session", SessionRoutes())
    .route("/permission", PermissionRoutes())
    .route("/question", QuestionRoutes())
    .route("/provider", ProviderRoutes())
    .route("/sync", SyncRoutes())
    .route("/", FileRoutes())
    .route("/", EventRoutes())
    .route("/mcp", McpRoutes())
    .route("/tui", TuiRoutes())
    // ── LTM (Long-Term Memory) ──
    .post(
      "/ltm/ingest",
      describeRoute({
        summary: "Store a memory in LTM",
        operationId: "ltm.ingest",
        responses: { 200: { description: "Memory stored" } },
      }),
      async (c) => {
        const body = await c.req.json<{ type: "episodic" | "semantic" | "procedural"; content: string; summary: string; projectID?: string; sessionID?: string; tags?: string[] }>()
        const embedderStatus = Embedder.status()
        if (!embedderStatus || embedderStatus.status !== "running") {
          return c.json({ error: "Embedding server not running" }, 503)
        }
        const vector = await Embedder.embedOne(body.summary)
        const id = `${body.type[0]}:${body.projectID ?? "global"}:${Date.now()}`
        const memory: LTM.Memory = {
          id,
          type: body.type,
          content: body.content.slice(0, 2000),
          summary: body.summary,
          vector,
          metadata: {
            source: `external:${body.projectID ?? "unknown"}`,
            timestamp: Date.now(),
            projectID: body.projectID,
            sessionID: body.sessionID,
            tags: body.tags ?? [],
          },
        }
        await LTM.Store.upsert(memory)
        return c.json({ id, success: true })
      },
    )
    .post(
      "/ltm/search",
      describeRoute({
        summary: "Search LTM memories by query",
        operationId: "ltm.search",
        responses: { 200: { description: "Search results" } },
      }),
      async (c) => {
        const body = await c.req.json<{ query: string; topK?: number; minScore?: number; type?: "episodic" | "semantic" | "procedural"; projectID?: string }>()
        const embedderStatus = Embedder.status()
        if (!embedderStatus || embedderStatus.status !== "running") {
          return c.json({ memories: [], count: 0 })
        }
        const vector = await Embedder.embedOne(body.query)
        const memories = await LTM.Store.search(vector, {
          topK: body.topK ?? 5,
          minScore: body.minScore ?? 0.7,
          type: body.type,
        })
        const filtered = body.projectID
          ? memories.filter((m) => m.metadata.projectID === body.projectID)
          : memories
        const lean = filtered.map((m) => ({
          id: m.id,
          type: m.type,
          summary: m.summary,
          content: m.content,
          score: m.score,
          metadata: { source: m.metadata.source, timestamp: m.metadata.timestamp, projectID: m.metadata.projectID, tags: m.metadata.tags },
        }))
        return c.json({ memories: lean, count: lean.length })
      },
    )
    .post(
      "/ltm/delete",
      describeRoute({
        summary: "Delete LTM memories by ID",
        operationId: "ltm.delete",
        responses: { 200: { description: "Deleted" } },
      }),
      async (c) => {
        const body = await c.req.json<{ ids: string[] }>()
        await LTM.Store.remove(body.ids)
        return c.json({ removed: body.ids.length })
      },
    )
    .get(
      "/ltm/status",
      describeRoute({
        summary: "Get LTM status",
        operationId: "ltm.status",
        responses: { 200: { description: "LTM status" } },
      }),
      async (c) => {
        const embedder = Embedder.status()
        const count = await LTM.Store.count()
        return c.json({ enabled: embedder?.status === "running", memoryCount: count })
      },
    )
    .post(
      "/instance/dispose",
      describeRoute({
        summary: "Dispose instance",
        description: "Clean up and dispose the current Saeeol instance, releasing all resources.",
        operationId: "instance.dispose",
        responses: {
          200: {
            description: "Instance disposed",
            content: {
              "application/json": {
                schema: resolver(z.boolean()),
              },
            },
          },
        },
      }),
      async (c) => {
        await InstanceStore.disposeInstance(Instance.current)
        return c.json(true)
      },
    )
    .get(
      "/path",
      describeRoute({
        summary: "Get paths",
        description: "Retrieve the current working directory and related path information for the Saeeol instance.",
        operationId: "path.get",
        responses: {
          200: {
            description: "Path",
            content: {
              "application/json": {
                schema: resolver(
                  z
                    .object({
                      home: z.string(),
                      state: z.string(),
                      config: z.string(),
                      worktree: z.string(),
                      directory: z.string(),
                    })
                    .meta({
                      ref: "Path",
                    }),
                ),
              },
            },
          },
        },
      }),
      async (c) => {
        return c.json({
          home: Global.Path.home,
          state: Global.Path.state,
          config: Global.Path.config,
          worktree: Instance.worktree,
          directory: Instance.directory,
        })
      },
    )
    .get(
      "/vcs",
      describeRoute({
        summary: "Get VCS info",
        description: "Retrieve version control system (VCS) information for the current project, such as git branch.",
        operationId: "vcs.get",
        responses: {
          200: {
            description: "VCS info",
            content: {
              "application/json": {
                schema: resolver(Vcs.Info.zod),
              },
            },
          },
        },
      }),
      async (c) =>
        jsonRequest("InstanceRoutes.vcs.get", c, function* () {
          const vcs = yield* Vcs.Service
          const [branch, default_branch] = yield* Effect.all([vcs.branch(), vcs.defaultBranch()], {
            concurrency: 2,
          })
          return { branch, default_branch }
        }),
    )
    .get(
      "/vcs/diff",
      describeRoute({
        summary: "Get VCS diff",
        description: "Retrieve the current git diff for the working tree or against the default branch.",
        operationId: "vcs.diff",
        responses: {
          200: {
            description: "VCS diff",
            content: {
              "application/json": {
                schema: resolver(Vcs.FileDiff.zod.array()),
              },
            },
          },
        },
      }),
      validator(
        "query",
        z.object({
          mode: Vcs.Mode.zod,
        }),
      ),
      async (c) =>
        jsonRequest("InstanceRoutes.vcs.diff", c, function* () {
          const vcs = yield* Vcs.Service
          return yield* vcs.diff(c.req.valid("query").mode)
        }),
    )
    .get(
      "/command",
      describeRoute({
        summary: "List commands",
        description: "Get a list of all available commands in the Saeeol system.",
        operationId: "command.list",
        responses: {
          200: {
            description: "List of commands",
            content: {
              "application/json": {
                schema: resolver(Command.Info.zod.array()),
              },
            },
          },
        },
      }),
      async (c) =>
        jsonRequest("InstanceRoutes.command.list", c, function* () {
          const svc = yield* Command.Service
          return yield* svc.list()
        }),
    )
    .get(
      "/agent",
      describeRoute({
        summary: "List agents",
        description: "Get a list of all available AI agents in the Saeeol system.",
        operationId: "app.agents",
        responses: {
          200: {
            description: "List of agents",
            content: {
              "application/json": {
                schema: resolver(Agent.Info.zod.array()),
              },
            },
          },
        },
      }),
      async (c) =>
        jsonRequest("InstanceRoutes.agent.list", c, function* () {
          const svc = yield* Agent.Service
          return yield* svc.list()
        }),
    )
    .get(
      "/skill",
      describeRoute({
        summary: "List skills",
        description: "Get a list of all available skills in the Saeeol system.",
        operationId: "app.skills",
        responses: {
          200: {
            description: "List of skills",
            content: {
              "application/json": {
                schema: resolver(Skill.Info.zod.array()),
              },
            },
          },
        },
      }),
      async (c) =>
        jsonRequest("InstanceRoutes.skill.list", c, function* () {
          const skill = yield* Skill.Service
          return yield* skill.all()
        }),
    )
    .get(
      "/lsp",
      describeRoute({
        summary: "Get LSP status",
        description: "Get LSP server status",
        operationId: "lsp.status",
        responses: {
          200: {
            description: "LSP server status",
            content: {
              "application/json": {
                schema: resolver(LSP.Status.zod.array()),
              },
            },
          },
        },
      }),
      async (c) =>
        jsonRequest("InstanceRoutes.lsp.status", c, function* () {
          const lsp = yield* LSP.Service
          return yield* lsp.status()
        }),
    )
    .get(
      "/formatter",
      describeRoute({
        summary: "Get formatter status",
        description: "Get formatter status",
        operationId: "formatter.status",
        responses: {
          200: {
            description: "Formatter status",
            content: {
              "application/json": {
                schema: resolver(Format.Status.zod.array()),
              },
            },
          },
        },
      }),
      async (c) =>
        jsonRequest("InstanceRoutes.formatter.status", c, function* () {
          const svc = yield* Format.Service
          return yield* svc.status()
        }),
    )

  return registerSaeeolRoutes(full) 
}
