import { Effect } from "effect"
import path from "path"
import { Flag } from "@saeeol/core/flag/flag"
import { PartID } from "../core/schema"
import { MessageV2 } from "../message/message-v2"
import * as Session from "../core/session"
import { InstanceState } from "@/effect/instance-state"
import CODE_SWITCH from "./code-switch.txt"
import type { PromptDeps } from "./prompt-types"
import * as LTM from "@/ltm"

const PLAN_REMINDER = `<system-reminder>
Plan mode is active. The user indicated that they do not want you to execute yet -- you MUST NOT make any edits (with the exception of the plan file mentioned below), run any non-readonly tools (including changing configs or making commits), or otherwise make any changes to the system. This supersedes any other instructions you have received.

## Plan File Info:
{{PLAN_INFO}}
You should build your plan incrementally by writing to or editing this file. NOTE that this is the only file you are allowed to edit -- you are only allowed to take READ-ONLY actions.

## Plan Workflow

### Phase 1: Initial Understanding
Goal: Gain a comprehensive understanding of the user's request by reading through code and asking them questions. Critical to In this phase you should only use the explore subagent type.

1. Focus on understanding the user's request and the code associated with their request
2. **Launch up to 3 explore agents IN PARALLEL** to efficiently explore the codebase.
3. After exploring the code, use the question tool to clarify ambiguities in the user request up front.

### Phase 2: Design
Goal: Design an implementation approach. Launch general agent(s) based on exploration results.

### Phase 3: Review
Goal: Review the plan(s) and ensure alignment with the user's intentions.

### Phase 4: Final Plan
Goal: Write your final plan to the plan file. Include recommended approach, critical file paths, and verification steps.

### Phase 5: Call plan_exit tool
At the end, call plan_exit to indicate you are done planning. Do NOT stop unless asking a question or calling plan_exit.
</system-reminder>`

export const insertReminders = (deps: PromptDeps, SaeeolSessionPrompt: any) =>
  Effect.fn("SessionPrompt.insertReminders")(function* (input: {
    messages: MessageV2.WithParts[]
    agent: any
    session: Session.Info
  }) {
    const userMessage = input.messages.findLast((msg) => msg.info.role === "user")
    if (!userMessage) return input.messages

    if (!Flag.SAEEOL_EXPERIMENTAL_PLAN_MODE) {
      yield* Effect.promise(() =>
        SaeeolSessionPrompt.insertPlanReminders({ agent: input.agent, session: input.session, userMessage }),
      )
      const wasPlan = input.messages.some((msg) => msg.info.role === "assistant" && msg.info.agent === "plan")
      if (wasPlan && input.agent.name === "code") {
        userMessage.parts.push({
          id: PartID.ascending(), messageID: userMessage.info.id,
          sessionID: userMessage.info.sessionID, type: "text", text: CODE_SWITCH, synthetic: true,
        })
      }

      // LTM retrieval — inject relevant long-term memories into the prompt
      const cfg = yield* deps.config.get()
      if (cfg?.ltm?.enabled) {
        const userText = userMessage.parts
          .filter((p) => p.type === "text")
          .map((p) => (p as any).text ?? "")
          .join(" ")
          .slice(0, 500)
        if (userText.length > 10) {
          const ltmCfg = {
            ...LTM.Config.DEFAULT_LTM_CONFIG,
            ...cfg.ltm,
            episodic: { ...LTM.Config.DEFAULT_LTM_CONFIG.episodic, ...cfg.ltm.episodic },
            semantic: { ...LTM.Config.DEFAULT_LTM_CONFIG.semantic, ...cfg.ltm.semantic },
            procedural: { ...LTM.Config.DEFAULT_LTM_CONFIG.procedural, ...cfg.ltm.procedural },
            retrieval: { ...LTM.Config.DEFAULT_LTM_CONFIG.retrieval, ...cfg.ltm.retrieval },
          }
          const context = yield* Effect.promise(() => LTM.Retrieval.inject(userText, ltmCfg))
          if (context) {
            userMessage.parts.push({
              id: PartID.ascending(), messageID: userMessage.info.id,
              sessionID: userMessage.info.sessionID, type: "text", text: `\n${context}`, synthetic: true,
            })
          }
        }
      }

      return input.messages
    }

    const assistantMessage = input.messages.findLast((msg) => msg.info.role === "assistant")
    if (input.agent.name !== "plan" && assistantMessage?.info.agent === "plan") {
      const ctx = yield* InstanceState.context
      const plan = Session.plan(input.session, ctx)
      if (!(yield* deps.fsys.existsSafe(plan))) return input.messages
      const part = yield* deps.sessions.updatePart({
        id: PartID.ascending(), messageID: userMessage.info.id, sessionID: userMessage.info.sessionID,
        type: "text", text: `${CODE_SWITCH}\n\nA plan file exists at ${plan}. You should execute on the plan defined within it`, synthetic: true,
      })
      userMessage.parts.push(part)
      return input.messages
    }

    if (input.agent.name !== "plan" || assistantMessage?.info.agent === "plan") return input.messages

    const ctx = yield* InstanceState.context
    const plan = Session.plan(input.session, ctx)
    const exists = yield* deps.fsys.existsSafe(plan)
    if (!exists) yield* deps.fsys.ensureDir(path.dirname(plan)).pipe(Effect.catch(Effect.die))
    const planInfo = exists
      ? `A plan file already exists at ${plan}. You can read it and make incremental edits using the edit tool.`
      : `No plan file exists yet. You should create your plan at ${plan} using the write tool.`
    const part = yield* deps.sessions.updatePart({
      id: PartID.ascending(), messageID: userMessage.info.id, sessionID: userMessage.info.sessionID,
      type: "text", text: PLAN_REMINDER.replace("{{PLAN_INFO}}", planInfo), synthetic: true,
    })
    userMessage.parts.push(part)
    return input.messages
  })
