;; Title: Miniforge.ai
;; Subtitle: An agentic SDLC / fleet-control platform
;; Author: Christopher Lester
;; Line: Founder, Miniforge.ai (project)
;; Copyright 2025-2026 Christopher Lester (christopher@miniforge.ai)
;;
;; Licensed under the Apache License, Version 2.0 (the "License");
;; you may not use this file except in compliance with the License.
;; You may obtain a copy of the License at
;;
;;     http://www.apache.org/licenses/LICENSE-2.0
;;
;; Unless required by applicable law or agreed to in writing, software
;; distributed under the License is distributed on an "AS IS" BASIS,
;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
;; See the License for the specific language governing permissions and
;; limitations under the License.

(ns ai.miniforge.agent.prompts
  "Agent prompt loading utilities.
   Loads system prompts from EDN resources for configurability."
  (:require
   [ai.miniforge.llm.interface :as llm]
   [ai.miniforge.response.interface :as response]
   [clojure.edn :as edn]
   [clojure.java.io :as io]))

;------------------------------------------------------------------------------ Layer 0
;; Prompt loading

(defn load-prompt
  "Load an agent prompt from the resources directory.

   This is the fail-fast startup boundary: prompt resources are
   compiled into the deploy artifact, so a missing or malformed
   resource is a build/packaging fault — not a runtime not-found
   condition. The boundary throw uses `:anomalies/fault` so callers
   that try+ on fault categories observe it consistently with the
   rest of the platform.

   Arguments:
   - agent-type: Keyword identifying the agent (e.g., :implementer, :planner)

   Returns:
   - The system prompt string

   Throws:
   - slingshot anomaly with `:anomaly/category :anomalies/fault`
     when the prompt resource is missing or missing the
     `:prompt/system` key."
  [agent-type]
  (let [resource-path (str "prompts/" (name agent-type) ".edn")]
    (if-let [resource (io/resource resource-path)]
      (let [prompt-data (with-open [rdr (io/reader resource)]
                          (edn/read (java.io.PushbackReader. rdr)))]
        (or (:prompt/system prompt-data)
            (response/throw-anomaly! :anomalies/fault
                                    "Prompt data missing :prompt/system key"
                                    {:agent-type agent-type
                                     :resource-path resource-path
                                     :keys (keys prompt-data)})))
      (response/throw-anomaly! :anomalies/fault
                                "Could not find prompt resource"
                                {:agent-type agent-type
                                 :resource-path resource-path}))))

(defn load-prompt-data
  "Load full prompt data map from resources.

   Boundary semantics match `load-prompt`: a missing resource is
   a build-time fault, not a runtime not-found. See `load-prompt`
   for the rationale on the `:anomalies/fault` category.

   Arguments:
   - agent-type: Keyword identifying the agent

   Returns:
   - The full prompt data map including :prompt/id, :prompt/version, etc.

   Throws:
   - slingshot anomaly with `:anomaly/category :anomalies/fault`
     when the prompt resource is missing on the classpath."
  [agent-type]
  (let [resource-path (str "prompts/" (name agent-type) ".edn")]
    (if-let [resource (io/resource resource-path)]
      (with-open [rdr (io/reader resource)]
        (edn/read (java.io.PushbackReader. rdr)))
      (response/throw-anomaly! :anomalies/fault
                                "Could not find prompt resource"
                                {:agent-type agent-type
                                 :resource-path resource-path}))))

;------------------------------------------------------------------------------ Layer 1
;; Progress-monitor factory

(defn load-progress-monitor
  "Build a progress monitor from a prompt-data map's monitor-config
   key. Returns nil when the key is absent so callers can `cond->`
   the option onto LLM opts only when configured.

   Single source of truth for the EDN → llm/create-progress-monitor
   adapter; planner and reviewer call this rather than each rolling
   their own factory.

   Arguments:
     prompt-data  - map returned by load-prompt-data
     monitor-key  - keyword pointing at the monitor config block
                    (e.g. :prompt/progress-monitor,
                          :prompt/submission-retry-monitor)

   Returns: progress-monitor atom, or nil when block is absent."
  [prompt-data monitor-key]
  (when-let [config (get prompt-data monitor-key)]
    (llm/create-progress-monitor config)))

;------------------------------------------------------------------------------ Rich Comment
(comment
  (load-prompt :implementer)
  (load-prompt :tester)
  (load-prompt :planner)
  (load-prompt :releaser)
  (load-prompt-data :implementer)
  (load-progress-monitor (load-prompt-data :reviewer)
                         :prompt/progress-monitor)
  :leave-this-here)
