/**
 * handlers/ltm.ts — LTM HTTP API handlers
 *
 * 외부 앱이 saeeol LTM에 기억을 저장/검색/삭제.
 * 임베딩은 로컬 서버가 처리, 클라이언트는 텍스트만 보냄.
 */

import { Effect } from "effect"
import * as Log from "@saeeol/core/util/log"
import * as HttpApiBuilder from "effect/unstable/httpapi"
import * as LTM from "@/ltm"
import * as Embedder from "@/provider/local/embedder"
import { LTMHttpApi } from "../groups/ltm"

const log = Log.create({ service: "server/handlers/ltm" })

export const ltmHandlers = HttpApiBuilder.group(LTMHttpApi, "ltm", (handlers) =>
  Effect.gen(function* () {
    const config = LTM.Config.DEFAULT_LTM_CONFIG

    return handlers
      .handle("ingest", ({ payload }) =>
        Effect.gen(function* () {
          if (!config.enabled) {
            yield* log.info("LTM ingest skipped — disabled")
            return HttpApiBuilder.fail({ status: 400 as const, body: { message: "LTM is disabled" } })
          }

          const embedderStatus = Embedder.status()
          if (!embedderStatus || embedderStatus.status !== "running") {
            yield* log.info("LTM ingest skipped — embedder not running")
            return HttpApiBuilder.fail({ status: 503 as const, body: { message: "Embedding server not running" } })
          }

          const vector = yield* Effect.tryPromise(() => Embedder.embedOne(payload.summary))
          const id = `${payload.type[0]}:${payload.projectID ?? "global"}:${Date.now()}`
          const memory: LTM.Memory = {
            id,
            type: payload.type,
            content: payload.content.slice(0, 2000),
            summary: payload.summary,
            vector,
            metadata: {
              source: `external:${payload.projectID ?? "unknown"}`,
              timestamp: Date.now(),
              projectID: payload.projectID,
              sessionID: payload.sessionID,
              tags: payload.tags ?? [],
            },
          }

          yield* Effect.tryPromise(() => LTM.Store.upsert(memory))
          yield* log.info("LTM ingest", { id, type: payload.type })

          return { id, success: true }
        }),
      )
      .handle("search", ({ payload }) =>
        Effect.gen(function* () {
          if (!config.enabled) {
            return { memories: [], count: 0 }
          }

          const embedderStatus = Embedder.status()
          if (!embedderStatus || embedderStatus.status !== "running") {
            return { memories: [], count: 0 }
          }

          const vector = yield* Effect.tryPromise(() => Embedder.embedOne(payload.query))
          const memories = yield* Effect.tryPromise(() =>
            LTM.Store.search(vector, {
              topK: payload.topK ?? config.retrieval.topK,
              minScore: payload.minScore ?? config.retrieval.minScore,
              type: payload.type,
            }),
          )

          // projectID 필터 (store 레벨에서 안 되면 여기서)
          const filtered = payload.projectID
            ? memories.filter((m) => m.metadata.projectID === payload.projectID)
            : memories

          // vector는 응답에서 제거 (용량 절약)
          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 { memories: lean, count: lean.length }
        }),
      )
      .handle("delete", ({ payload }) =>
        Effect.gen(function* () {
          yield* Effect.tryPromise(() => LTM.Store.remove(payload.ids))
          return { removed: payload.ids.length }
        }),
      )
      .handle("status", () =>
        Effect.gen(function* () {
          const embedder = Embedder.status()
          const count = yield* Effect.tryPromise(() => LTM.Store.count())
          return {
            enabled: config.enabled && embedder?.status === "running",
            memoryCount: count,
          }
        }),
      )
  }),
)
