import { Pty } from "@opencode-ai/core/pty"
import { PtyTicket } from "@opencode-ai/core/pty/ticket"
import { PtyID } from "@opencode-ai/core/pty/schema"
import { PTY_CONNECT_TICKET_QUERY } from "@/server/shared/pty-ticket"
import { Schema } from "effect"
import { HttpApi, HttpApiEndpoint, HttpApiError, HttpApiGroup, OpenApi } from "effect/unstable/httpapi"
import { Authorization, PtyConnectAuthorization } from "../middleware/authorization"
import { InstanceContextMiddleware } from "../middleware/instance-context"
import {
  WorkspaceRoutingMiddleware,
  WorkspaceRoutingQuery,
  WorkspaceRoutingQueryFields,
} from "../middleware/workspace-routing"
import { PtyForbiddenError, PtyNotFoundError } from "../errors"
import { described } from "./metadata"

const root = "/pty"
export const Params = Schema.Struct({ ptyID: PtyID })
export const CursorQuery = Schema.Struct({
  ...WorkspaceRoutingQueryFields,
  cursor: Schema.optional(Schema.String),
})
export const ShellItem = Schema.Struct({
  path: Schema.String,
  name: Schema.String,
  acceptable: Schema.Boolean,
})

export const PtyPaths = {
  shells: `${root}/shells`,
  list: root,
  create: root,
  get: `${root}/:ptyID`,
  update: `${root}/:ptyID`,
  remove: `${root}/:ptyID`,
  connectToken: `${root}/:ptyID/connect-token`,
  connect: `${root}/:ptyID/connect`,
} as const

export const PtyApi = HttpApi.make("pty")
  .add(
    HttpApiGroup.make("pty")
      .add(
        HttpApiEndpoint.get("shells", PtyPaths.shells, {
          query: WorkspaceRoutingQuery,
          success: described(Schema.Array(ShellItem), "List of shells"),
        }).annotateMerge(
          OpenApi.annotations({
            identifier: "pty.shells",
            summary: "List available shells",
            description: "Get a list of available shells on the system.",
          }),
        ),
        HttpApiEndpoint.get("list", PtyPaths.list, {
          query: WorkspaceRoutingQuery,
          success: described(Schema.Array(Pty.Info), "List of sessions"),
        }).annotateMerge(
          OpenApi.annotations({
            identifier: "pty.list",
            summary: "List PTY sessions",
            description: "Get a list of all active pseudo-terminal (PTY) sessions managed by OpenCode.",
          }),
        ),
        HttpApiEndpoint.post("create", PtyPaths.create, {
          query: WorkspaceRoutingQuery,
          payload: Pty.CreateInput,
          success: described(Pty.Info, "Created session"),
          error: HttpApiError.BadRequest,
        }).annotateMerge(
          OpenApi.annotations({
            identifier: "pty.create",
            summary: "Create PTY session",
            description: "Create a new pseudo-terminal (PTY) session for running shell commands and processes.",
          }),
        ),
        HttpApiEndpoint.get("get", PtyPaths.get, {
          params: { ptyID: PtyID },
          query: WorkspaceRoutingQuery,
          success: described(Pty.Info, "Session info"),
          error: PtyNotFoundError,
        }).annotateMerge(
          OpenApi.annotations({
            identifier: "pty.get",
            summary: "Get PTY session",
            description: "Retrieve detailed information about a specific pseudo-terminal (PTY) session.",
          }),
        ),
        HttpApiEndpoint.put("update", PtyPaths.update, {
          params: { ptyID: PtyID },
          query: WorkspaceRoutingQuery,
          payload: Pty.UpdateInput,
          success: described(Pty.Info, "Updated session"),
          error: [PtyNotFoundError, HttpApiError.BadRequest],
        }).annotateMerge(
          OpenApi.annotations({
            identifier: "pty.update",
            summary: "Update PTY session",
            description: "Update properties of an existing pseudo-terminal (PTY) session.",
          }),
        ),
        HttpApiEndpoint.delete("remove", PtyPaths.remove, {
          params: { ptyID: PtyID },
          query: WorkspaceRoutingQuery,
          success: described(Schema.Boolean, "Session removed"),
          error: PtyNotFoundError,
        }).annotateMerge(
          OpenApi.annotations({
            identifier: "pty.remove",
            summary: "Remove PTY session",
            description: "Remove and terminate a specific pseudo-terminal (PTY) session.",
          }),
        ),
        HttpApiEndpoint.post("connectToken", PtyPaths.connectToken, {
          params: { ptyID: PtyID },
          query: WorkspaceRoutingQuery,
          success: described(PtyTicket.ConnectToken, "WebSocket connect token"),
          error: [PtyForbiddenError, PtyNotFoundError],
        }).annotateMerge(
          OpenApi.annotations({
            identifier: "pty.connectToken",
            summary: "Create PTY WebSocket token",
            description: "Create a short-lived ticket for opening a PTY WebSocket connection.",
          }),
        ),
      )
      .annotateMerge(OpenApi.annotations({ title: "pty", description: "Experimental HttpApi PTY routes." }))
      .middleware(InstanceContextMiddleware)
      .middleware(WorkspaceRoutingMiddleware)
      .middleware(Authorization),
  )
  .annotateMerge(
    OpenApi.annotations({
      title: "opencode experimental HttpApi",
      version: "0.0.1",
      description: "Experimental HttpApi surface for selected instance routes.",
    }),
  )

export const PtyConnectApi = HttpApi.make("pty-connect").add(
  HttpApiGroup.make("pty-connect")
    .add(
      // Decode PTY connection query fields in the raw handler after checking
      // existence, preserving the established empty-404 response ordering.
      HttpApiEndpoint.get("connect", PtyPaths.connect, {
        params: Params,
        success: described(Schema.Boolean, "Connected session"),
        error: [HttpApiError.Forbidden, HttpApiError.NotFound],
      }).annotateMerge(
        OpenApi.annotations({
          identifier: "pty.connect",
          summary: "Connect to PTY session",
          description:
            "Establish a WebSocket connection to interact with a pseudo-terminal (PTY) session in real-time.",
          transform: (operation) => ({
            ...operation,
            parameters: [
              ...(operation.parameters ?? []),
              ...["directory", "workspace", "cursor", PTY_CONNECT_TICKET_QUERY].map((name) => ({
                in: "query",
                name,
                schema: { type: "string" },
              })),
            ],
          }),
        }),
      ),
    )
    .annotateMerge(OpenApi.annotations({ title: "pty", description: "PTY websocket route." }))
    .middleware(InstanceContextMiddleware)
    .middleware(WorkspaceRoutingMiddleware)
    .middleware(PtyConnectAuthorization),
)
