use std::path::{Path, PathBuf};
use std::{env, io};

use chrono::Utc;
use serde_json::{Value, json};

use crate::app;
use crate::storage::{AppResult, Storage};

const SELF_REFLECTION_REQUEST_MARKER: &str = "MENTAT_SELF_REFLECTION_REQUEST_JSON:\n";
const SELF_REFLECTION_DECISION_MARKER: &str = "MENTAT_SELF_REFLECTION_DECISION_JSON:\n";
const RECENT_TRANSCRIPT_WINDOW: usize = 24;

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MentatDecision {
    Ignore,
    Review,
}

impl MentatDecision {
    pub fn as_str(self) -> &'static str {
        match self {
            Self::Ignore => "ignore",
            Self::Review => "review",
        }
    }
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct MentatSelfReflectionContext {
    pub raw_user_request: String,
    pub working_dir: PathBuf,
    pub recent_transcript: Vec<String>,
    pub worker_final_reply: String,
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct MentatSelfReflectionDecision {
    pub decision: MentatDecision,
    pub reason: String,
    pub display_text: String,
}

pub fn build_self_reflection_prompt(context: &MentatSelfReflectionContext) -> AppResult<String> {
    let mut recent_transcript = context.recent_transcript.clone();
    if recent_transcript.len() > RECENT_TRANSCRIPT_WINDOW {
        recent_transcript =
            recent_transcript.split_off(recent_transcript.len() - RECENT_TRANSCRIPT_WINDOW);
    }

    let request = json!({
        "raw_user_request": context.raw_user_request,
        "working_dir": context.working_dir,
        "recent_transcript": recent_transcript,
        "worker_final_reply": context.worker_final_reply,
    });

    Ok(format!(
        concat!(
            "You are Mentat performing post-run self reflection for a completed worker run.\n",
            "Default to ignore.\n",
            "Only choose review when the evidence strongly suggests the user's execution goal was not actually completed or a follow-up reflective review is likely to materially improve the trajectory.\n",
            "Never escalate pure Q&A, simple lookup, or ordinary review requests.\n",
            "Use only the provided evidence.\n",
            "First write concise user-visible markdown.\n",
            "Then append a machine-readable block exactly in this form:\n",
            "{marker}{{\"decision\":\"ignore\",\"reason\":\"brief reason\"}}\n\n",
            "{request_marker}{}"
        ),
        serde_json::to_string_pretty(&request)?,
        marker = SELF_REFLECTION_DECISION_MARKER,
        request_marker = SELF_REFLECTION_REQUEST_MARKER,
    ))
}

pub fn parse_self_reflection_output(output: &str) -> AppResult<MentatSelfReflectionDecision> {
    let (display_text, decision_json) = output
        .split_once(SELF_REFLECTION_DECISION_MARKER)
        .ok_or_else(|| {
            io::Error::new(
                io::ErrorKind::InvalidData,
                "Mentat self reflection output missing decision marker",
            )
        })?;
    let decision_value: Value = serde_json::from_str(decision_json.trim())?;
    let decision = match decision_value
        .get("decision")
        .and_then(Value::as_str)
        .unwrap_or("")
    {
        "ignore" => MentatDecision::Ignore,
        "review" => MentatDecision::Review,
        other => {
            return Err(io::Error::new(
                io::ErrorKind::InvalidData,
                format!("unknown Mentat self reflection decision {other}"),
            )
            .into());
        }
    };
    let reason = decision_value
        .get("reason")
        .and_then(Value::as_str)
        .unwrap_or("")
        .trim()
        .to_string();
    Ok(MentatSelfReflectionDecision {
        decision,
        reason,
        display_text: display_text.trim().to_string(),
    })
}

pub fn run_self_reflection_decision(
    working_dir: &Path,
    context: &MentatSelfReflectionContext,
) -> AppResult<MentatSelfReflectionDecision> {
    let storage = Storage::from_env()?;
    let raw_event_path = env::temp_dir().join(format!(
        "mentat-self-reflection-{}-{}.ndjson",
        std::process::id(),
        Utc::now().timestamp_micros()
    ));
    let prompt = build_self_reflection_prompt(context)?;
    let answer =
        app::direct_self_reflection(&storage, working_dir, &prompt, &raw_event_path, None)?;
    let _ = std::fs::remove_file(&raw_event_path);
    parse_self_reflection_output(&answer)
}
