import { Effect } from "effect"
import { MessageV2 } from "@/session/message-v2"
import { PartID } from "@/session/schema"
import * as Log from "@saeeol/core/util/log"
import {
  type Input,
  type Batch,
  CONCURRENCY,
  DEFAULT_AUTO_BATCHES,
  TOOL_OUTPUT_MAX_CHARS,
  batchSplit,
  budget,
  eligible as eligibleImpl,
  large,
  needed as neededImpl,
  transcript,
} from "./compaction-chunks-utils"
import { processBatch, replay as replayImpl, run } from "./compaction-chunks-core"

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

const autoPrompt = [
  "Summarize the oldest conversation history below.",
  "This summary will replace those messages in context, so preserve all important facts.",
  "Keep concrete file paths, commands, errors, decisions, and unresolved tasks.",
  "Use terse Markdown bullets. Do not mention compaction or summarization.",
].join("\n")

function manualProcess(input: Input, batches: Batch[]) {
  return Effect.gen(function* () {
    log.info("manual batch compaction", { batches: batches.length })
    const results = yield* Effect.forEach(
      batches,
      (b) => processBatch({ ...input, b, total: batches.length }),
      { concurrency: Math.min(CONCURRENCY, batches.length) },
    )
    if (results.some((r) => r.result !== "continue" || !r.output)) return "compact" as const
    const final = results.map((r) => r.output!).join("\n\n")
    yield* input.updatePart({
      id: PartID.ascending(),
      messageID: input.target.id,
      sessionID: input.sessionID,
      type: "text",
      text: final,
    })
    input.target.finish = "stop"
    input.target.error = undefined
    input.target.time.completed = Date.now()
    yield* input.updateMessage(input.target)
    return "continue" as const
  })
}

function autoProcess(input: Input, batches: Batch[]) {
  return Effect.gen(function* () {
    const autoLimit = input.cfg.compaction?.auto_batches ?? DEFAULT_AUTO_BATCHES
    const limit = Math.min(autoLimit, batches.length)
    const oldest = batches.slice(0, limit)
    const rest = batches.slice(limit)

    if (input.out && rest.length) {
      const first = rest[0]?.first[0]
      if (first) input.out.tail_start_id = first.info.id
    }

    const autoMessages = oldest.flatMap((b) => [...b.first, ...b.middle, ...b.last])
    log.info("auto batch compaction", { total: batches.length, oldest: limit, messages: autoMessages.length })

    const size = budget({ cfg: input.cfg, model: input.model })
    const tooLarge = yield* large({ messages: autoMessages, model: input.model, size })
    const data = tooLarge
      ? [
          {
            role: "user" as const,
            content: [
              {
                type: "text" as const,
                text: [
                  "The following compacted transcript represents an oversized conversation section.",
                  "Summarize all facts present in the transcript.",
                  "<conversation>",
                  transcript({ messages: autoMessages }),
                  "</conversation>",
                ].join("\n"),
              },
            ],
          },
        ]
      : yield* MessageV2.toModelMessagesEffect(autoMessages, input.model, {
          stripMedia: true,
          toolOutputMaxChars: TOOL_OUTPUT_MAX_CHARS,
        })

    const result = yield* run({ ...input, data, text: autoPrompt })
    if (result.result !== "continue" || !result.output) return "compact" as const

    yield* input.updatePart({
      id: PartID.ascending(),
      messageID: input.target.id,
      sessionID: input.sessionID,
      type: "text",
      text: result.output,
    })
    input.target.finish = "stop"
    input.target.error = undefined
    input.target.time.completed = Date.now()
    yield* input.updateMessage(input.target)
    return "continue" as const
  })
}

export namespace SaeeolCompactionChunks {
  export const eligible = eligibleImpl
  export const needed = neededImpl
  export const replay = replayImpl

  export function process(input: Input) {
    return Effect.gen(function* () {
      const batches = batchSplit(input.messages, input.cfg)
      if (!batches.length) return "compact" as const
      if (input.auto) return yield* autoProcess(input, batches)
      return yield* manualProcess(input, batches)
    })
  }
}
