import { Hono, describeRoute, validator, resolver, z, Session, SessionShare, SessionStatus, Todo, Permission, SessionID, errors, jsonRequest, runRequest, QueryBoolean, queryBoolean } from "./_shared"

export const crudRoutes = new Hono()
  .get(
    "/",
    describeRoute({
      summary: "List sessions",
      description: "Get a list of all Saeeol sessions, sorted by most recently updated.",
      operationId: "session.list",
      responses: {
        200: {
          description: "List of sessions",
          content: {
            "application/json": {
              schema: resolver(Session.Info.zod.array()),
            },
          },
        },
      },
    }),
    validator(
      "query",
      z.object({
        directory: z.string().optional().meta({ description: "Filter sessions by directory" }),
        scope: z.enum(["project"]).optional().meta({ description: "List all sessions for the current project" }),
        path: z.string().optional().meta({ description: "Filter sessions by project-relative path" }),
        roots: QueryBoolean.optional().meta({ description: "Only return root sessions (no parentID)" }),
        start: z.coerce
          .number()
          .optional()
          .meta({ description: "Filter sessions updated on or after this timestamp (milliseconds since epoch)" }),
        search: z.string().optional().meta({ description: "Filter sessions by title (case-insensitive)" }),
        limit: z.coerce.number().optional().meta({ description: "Maximum number of sessions to return" }),
      }),
    ),
    async (c) => {
      const query = c.req.valid("query")
      return c.json(
        await runRequest(
          "SessionRoutes.list",
          c,
          Session.Service.use((svc) =>
            svc.list({
              directory: query.scope === "project" ? undefined : query.directory,
              path: query.path,
              roots: queryBoolean(query.roots),
              start: query.start,
              search: query.search,
              limit: query.limit,
            }),
          ),
        ),
      )
    },
  )
  .get(
    "/status",
    describeRoute({
      summary: "Get session status",
      description: "Retrieve the current status of all sessions, including active, idle, and completed states.",
      operationId: "session.status",
      responses: {
        200: {
          description: "Get session status",
          content: {
            "application/json": {
              schema: resolver(z.record(z.string(), SessionStatus.Info.zod)),
            },
          },
        },
        ...errors(400),
      },
    }),
    async (c) =>
      jsonRequest("SessionRoutes.status", c, function* () {
        const svc = yield* SessionStatus.Service
        return Object.fromEntries(yield* svc.list())
      }),
  )
  .get(
    "/:sessionID",
    describeRoute({
      summary: "Get session",
      description: "Retrieve detailed information about a specific Saeeol session.",
      tags: ["Session"],
      operationId: "session.get",
      responses: {
        200: {
          description: "Get session",
          content: {
            "application/json": {
              schema: resolver(Session.Info.zod),
            },
          },
        },
        ...errors(400, 404),
      },
    }),
    validator(
      "param",
      z.object({
        sessionID: Session.GetInput.zod,
      }),
    ),
    async (c) => {
      const sessionID = c.req.valid("param").sessionID
      return jsonRequest("SessionRoutes.get", c, function* () {
        const session = yield* Session.Service
        return yield* session.get(sessionID)
      })
    },
  )
  .get(
    "/:sessionID/children",
    describeRoute({
      summary: "Get session children",
      tags: ["Session"],
      description: "Retrieve all child sessions that were forked from the specified parent session.",
      operationId: "session.children",
      responses: {
        200: {
          description: "List of children",
          content: {
            "application/json": {
              schema: resolver(Session.Info.zod.array()),
            },
          },
        },
        ...errors(400, 404),
      },
    }),
    validator(
      "param",
      z.object({
        sessionID: Session.ChildrenInput.zod,
      }),
    ),
    async (c) => {
      const sessionID = c.req.valid("param").sessionID
      return jsonRequest("SessionRoutes.children", c, function* () {
        const session = yield* Session.Service
        return yield* session.children(sessionID)
      })
    },
  )
  .get(
    "/:sessionID/todo",
    describeRoute({
      summary: "Get session todos",
      description: "Retrieve the todo list associated with a specific session, showing tasks and action items.",
      operationId: "session.todo",
      responses: {
        200: {
          description: "Todo list",
          content: {
            "application/json": {
              schema: resolver(Todo.Info.zod.array()),
            },
          },
        },
        ...errors(400, 404),
      },
    }),
    validator(
      "param",
      z.object({
          sessionID: SessionID.zod,
        }),
    ),
    async (c) => {
      const sessionID = c.req.valid("param").sessionID
      return jsonRequest("SessionRoutes.todo", c, function* () {
        const todo = yield* Todo.Service
        return yield* todo.get(sessionID)
      })
    },
  )
  .post(
    "/",
    describeRoute({
      summary: "Create session",
      description: "Create a new Saeeol session for interacting with AI assistants and managing conversations.",
      operationId: "session.create",
      responses: {
        ...errors(400),
        200: {
          description: "Successfully created session",
          content: {
            "application/json": {
              schema: resolver(Session.Info.zod),
            },
          },
        },
      },
    }),
    validator("json", Session.CreateInput.zod),
    async (c) =>
      jsonRequest("SessionRoutes.create", c, function* () {
        const body = c.req.valid("json") ?? {}
        const svc = yield* SessionShare.Service
        return yield* svc.create(body)
      }),
  )
  .delete(
    "/:sessionID",
    describeRoute({
      summary: "Delete session",
      description: "Delete a session and permanently remove all associated data, including messages and history.",
      operationId: "session.delete",
      responses: {
        200: {
          description: "Successfully deleted session",
          content: {
            "application/json": {
              schema: resolver(z.boolean()),
            },
          },
        },
        ...errors(400, 404),
      },
    }),
    validator(
      "param",
      z.object({
        sessionID: Session.RemoveInput.zod,
      }),
    ),
    async (c) =>
      jsonRequest("SessionRoutes.delete", c, function* () {
        const sessionID = c.req.valid("param").sessionID
        const svc = yield* Session.Service
        yield* svc.remove(sessionID)
        return true
      }),
  )
  .patch(
    "/:sessionID",
    describeRoute({
      summary: "Update session",
      description: "Update properties of an existing session, such as title or other metadata.",
      operationId: "session.update",
      responses: {
        200: {
          description: "Successfully updated session",
          content: {
            "application/json": {
              schema: resolver(Session.Info.zod),
            },
          },
        },
        ...errors(400, 404),
      },
    }),
    validator(
      "param",
      z.object({
          sessionID: SessionID.zod,
        }),
    ),
    validator(
      "json",
      z.object({
        title: z.string().optional(),
        permission: Permission.Ruleset.zod.optional(),
        time: z
          .object({
            archived: z.number().optional(),
          })
          .optional(),
      }),
    ),
    async (c) =>
      jsonRequest("SessionRoutes.update", c, function* () {
        const sessionID = c.req.valid("param").sessionID
        const updates = c.req.valid("json")
        const session = yield* Session.Service
        const current = yield* session.get(sessionID)

        if (updates.title !== undefined) {
          yield* session.setTitle({ sessionID, title: updates.title })
        }
        if (updates.permission !== undefined) {
          yield* session.setPermission({
            sessionID,
            permission: Permission.merge(current.permission ?? [], updates.permission),
          })
        }
        if (updates.time?.archived !== undefined) {
          yield* session.setArchived({ sessionID, time: updates.time.archived })
        }

        return yield* session.get(sessionID)
      }),
  )
