import { Effect, Exit, Cause, Option, Types } from "effect"
import { SessionID, MessageID, PartID } from "../core/schema"
import { MessageV2 } from "../message/message-v2"
import { NamedError } from "@saeeol/core/util/error"
import * as Log from "@saeeol/core/util/log"
import type { PromptInput } from "./prompt-schemas"
import type { ModelID, ProviderID } from "../../provider/schema"
import type { PromptDeps } from "./prompt-types"

const log = Log.create({ service: "session.prompt" })

export const getModel = (deps: PromptDeps) =>
  Effect.fn("SessionPrompt.getModel")(function* (providerID: ProviderID, modelID: ModelID, sessionID: SessionID) {
    const exit = yield* deps.provider.getModel(providerID, modelID).pipe(Effect.exit)
    if (Exit.isSuccess(exit)) return exit.value
    const err = Cause.squash(exit.cause)
    if (deps.provider.ModelNotFoundError?.isInstance?.(err) ?? (err as any).constructor?.name === "ModelNotFoundError") {
      const hint = (err as any).data?.suggestions?.length ? ` Did you mean: ${(err as any).data.suggestions.join(", ")}?` : ""
      yield* deps.bus.publish(deps.sessions.constructor?.Event?.Error ?? { type: "session.error" }, {
        sessionID,
        error: new NamedError.Unknown({ message: `Model not found: ${providerID}/${modelID}.${hint}` }).toObject(),
      })
    }
    return yield* Effect.failCause(exit.cause)
  })

export const lastModel = (deps: PromptDeps) =>
  Effect.fnUntraced(function* (sessionID: SessionID) {
    const match = yield* deps.sessions.findMessage(sessionID, (m: any) => m.info.role === "user" && !!m.info.model) as any
    if (Option.isSome(match) && (match.value as any).info.role === "user") return (match.value as any).info.model
    return yield* deps.provider.defaultModel()
  })

export const lastAssistant = (deps: PromptDeps) =>
  Effect.fnUntraced(function* (sessionID: SessionID) {
    for (let attempt = 0; attempt < 10; attempt++) {
      const match = yield* deps.sessions.findMessage(sessionID, (m: any) => m.info.role !== "user")
      if (Option.isSome(match)) return match.value
      const msgs = yield* deps.sessions.messages({ sessionID, limit: 1 })
      if (msgs.length > 0) return msgs[0]
      yield* Effect.sleep("50 millis")
    }
    throw new Error("Impossible")
  })
