import { Effect } from "effect"
import * as Log from "@saeeol/core/util/log"
import { MessageV2 } from "@/session/message-v2"
import { MessageID, PartID, type SessionID } from "@/session/schema"
import type { Session } from "@/session/session"
import { InstanceState } from "@/effect/instance-state"
import { readDigest, type Digest } from "./digest-storage"

const log = Log.create({ service: "saeeol.kg-compaction" })

type Input = {
  session: Pick<Session.Interface, "updateMessage" | "updatePart">
  sessionID: SessionID
  parentID: string
  variant: string
  agent: string
  model: { providerID: string; modelID: string }
}

function formatDigest(digest: Digest): string {
  const lines: string[] = []
  lines.push("# Session Memory (Knowledge Graph Digest)")
  lines.push("")
  lines.push(`Last updated: ${new Date(digest.updated).toISOString()}`)
  lines.push("")

  if (digest.summary) {
    lines.push("## Summary")
    lines.push(digest.summary)
    lines.push("")
  }

  for (const entity of digest.entities) {
    lines.push(`## ${entity.name} [${entity.type}]`)
    for (const obs of entity.observations) {
      lines.push(`- ${obs}`)
    }
    lines.push("")
  }

  if (digest.relations?.length) {
    lines.push("## Relations")
    for (const rel of digest.relations) {
      lines.push(`- ${rel.from} —${rel.type}→ ${rel.to}`)
    }
  }

  return lines.join("\n")
}

export namespace KgCompaction {
  export function tryKgCompaction(input: Input) {
    return Effect.gen(function* () {
      const digest = readDigest()

      if (!digest) {
        log.info("no digest found, falling back to LLM compaction")
        return null
      }

      if (!digest.entities.length) {
        log.info("empty digest, falling back to LLM compaction")
        return null
      }

      const age = Date.now() - digest.updated
      const maxAge = 30 * 60 * 1000
      if (age > maxAge) {
        log.info("digest too old, falling back", { ageMin: Math.round(age / 60_000) })
        return null
      }

      const ctx = yield* InstanceState.context
      const summaryText = formatDigest(digest)
      const content = [
        "This session is being continued. The Knowledge Graph digest below covers accumulated project memory.",
        "",
        summaryText,
        "",
        "Continue the conversation from where it left off.",
      ].join("\n")

      const userMsg: MessageV2.User = {
        id: MessageID.ascending(),
        role: "user",
        sessionID: input.sessionID,
        time: { created: Date.now() },
        agent: input.agent,
        model: input.model as any,
      }
      yield* input.session.updateMessage(userMsg)

      yield* input.session.updatePart({
        id: PartID.ascending(),
        messageID: userMsg.id,
        sessionID: input.sessionID,
        type: "text",
        synthetic: true,
        text: content,
        time: { start: Date.now(), end: Date.now() },
      } satisfies MessageV2.TextPart)

      const assistantMsg: MessageV2.Assistant = {
        id: MessageID.ascending(),
        role: "assistant",
        parentID: userMsg.id,
        sessionID: input.sessionID,
        mode: "compaction",
        agent: "compaction",
        variant: input.variant as any,
        summary: true,
        path: { cwd: ctx.directory, root: ctx.worktree },
        cost: 0,
        tokens: { output: 0, input: 0, reasoning: 0, cache: { read: 0, write: 0 } },
        time: { created: Date.now(), completed: Date.now() },
        finish: "stop",
        modelID: "" as any,
        providerID: "" as any,
      }
      yield* input.session.updateMessage(assistantMsg)

      yield* input.session.updatePart({
        id: PartID.ascending(),
        messageID: assistantMsg.id,
        sessionID: input.sessionID,
        type: "text",
        text: "⊟ Compacted via Knowledge Graph (no LLM call)",
      } satisfies MessageV2.TextPart)

      log.info("KG compaction complete", {
        entities: digest.entities.length,
        ageSec: Math.round(age / 1000),
      })

      return "continue" as const
    })
  }
}
