use std::ffi::{OsStr, OsString};
use std::fs;
use std::os::unix::fs::PermissionsExt;
use std::path::Path;

use mentai_agent_core::model::{
    SessionAgentRole, SessionImprovementRecord, SessionImprovementStatus, SessionMentatPhase,
    SessionMentatState, SessionMentatStatus, SessionMentatTarget,
};
use mentai_agent_core::session::SessionController;
use mentai_agent_core::storage::Storage;
use serde_json::Value;
use tempfile::TempDir;

#[test]
fn session_storage_creates_session_with_empty_intervention_state() {
    let tempdir = TempDir::new().expect("create tempdir");
    let root = tempdir.path().join("mentat-root");
    let workspace = tempdir.path().join("workspace");
    fs::create_dir_all(&root).expect("create mentat root");
    fs::create_dir_all(&workspace).expect("create workspace");

    let storage = Storage::from_root(root.clone());

    let session = storage
        .create_session(workspace.clone())
        .expect("create session");

    let session_path = root
        .join("sessions")
        .join(&session.session_id)
        .join("session.json");
    let transcript_path = root
        .join("sessions")
        .join(&session.session_id)
        .join("transcript.ndjson");

    assert!(session_path.exists(), "missing {}", session_path.display());
    assert!(
        transcript_path.exists(),
        "missing {}",
        transcript_path.display()
    );
    assert_eq!(session.cwd, workspace);
    assert!(session.foreground_turn_id.is_none());
    assert!(session.mentat.is_none());

    let raw_session: Value =
        serde_json::from_str(&fs::read_to_string(&session_path).expect("read session json"))
            .expect("parse session json");
    assert_eq!(raw_session.get("foreground_turn_id"), Some(&Value::Null));
    assert_eq!(raw_session.get("mentat"), Some(&Value::Null));
}

#[test]
fn session_mentat_state_deserializes_from_persisted_json() {
    let state: SessionMentatState = serde_json::from_value(serde_json::json!({
        "status": "intervening",
        "phase": "planning",
        "target": {
            "raw_user_request": "Review this workspace.",
            "goal": "Review this workspace",
            "spec_path": null
        },
        "source": "explicit"
    }))
    .expect("deserialize mentat state");

    assert_eq!(state.status, SessionMentatStatus::Intervening);
    assert_eq!(state.phase, Some(SessionMentatPhase::Planning));
}

#[test]
fn session_submit_answers_direct_questions_without_entering_mentat() {
    let tempdir = TempDir::new().expect("create tempdir");
    let root = tempdir.path().join("mentat-root");
    let workspace = tempdir.path().join("workspace");
    fs::create_dir_all(&root).expect("create mentat root");
    fs::create_dir_all(&workspace).expect("create workspace");
    let codex_bin = write_session_fake_codex_bin(tempdir.path());
    let _codex_bin_guard = EnvVarGuard::set("MENTAI_CODEX_BIN", codex_bin.as_os_str());

    let storage = Storage::from_root(root.clone());
    let session = storage
        .create_session(workspace.clone())
        .expect("create session");
    let controller = SessionController::new(storage.clone());

    let outcome = controller
        .submit_input(&session.session_id, "What is Mentat?")
        .expect("submit direct question");

    assert_eq!(
        outcome.answer.as_deref(),
        Some("Mentat is an AI reflection harness.")
    );
    assert!(outcome.mentat.is_none());
    assert_eq!(outcome.completed_agent_role, Some(SessionAgentRole::Worker));

    let updated = storage
        .load_session(&session.session_id)
        .expect("reload session");
    assert!(updated.foreground_turn_id.is_none());
    assert!(updated.mentat.is_none());

    let transcript = fs::read_to_string(
        root.join("sessions")
            .join(&session.session_id)
            .join("transcript.ndjson"),
    )
    .expect("read transcript");
    let transcript_lines = transcript
        .lines()
        .map(|line| serde_json::from_str::<Value>(line).expect("parse transcript line"))
        .collect::<Vec<_>>();
    assert_eq!(transcript_lines.len(), 2);
    assert_eq!(
        transcript_lines[0].get("kind").and_then(Value::as_str),
        Some("user_message")
    );
    assert_eq!(transcript_lines[0].get("agent_role"), Some(&Value::Null));
    assert_eq!(
        transcript_lines[0].get("content").and_then(Value::as_str),
        Some("What is Mentat?")
    );
    assert_eq!(
        transcript_lines[1].get("kind").and_then(Value::as_str),
        Some("agent_message")
    );
    assert_eq!(
        transcript_lines[1]
            .get("agent_role")
            .and_then(Value::as_str),
        Some("worker")
    );
    assert_eq!(
        transcript_lines[1].get("content").and_then(Value::as_str),
        Some("Mentat is an AI reflection harness.")
    );
}

#[test]
fn session_submit_keeps_plain_statements_ai_first() {
    let tempdir = TempDir::new().expect("create tempdir");
    let root = tempdir.path().join("mentat-root");
    let workspace = tempdir.path().join("workspace");
    fs::create_dir_all(&root).expect("create mentat root");
    fs::create_dir_all(&workspace).expect("create workspace");
    let codex_bin = write_session_fake_codex_bin(tempdir.path());
    let _codex_bin_guard = EnvVarGuard::set("MENTAI_CODEX_BIN", codex_bin.as_os_str());

    let storage = Storage::from_root(root.clone());
    let session = storage
        .create_session(workspace.clone())
        .expect("create session");
    let controller = SessionController::new(storage.clone());

    let outcome = controller
        .submit_input(&session.session_id, "hello there")
        .expect("submit plain statement");

    assert_eq!(outcome.answer.as_deref(), Some("conversation"));
    assert!(outcome.mentat.is_none());

    let updated = storage
        .load_session(&session.session_id)
        .expect("reload session");
    assert!(updated.mentat.is_none());
}

#[test]
fn explicit_mentat_request_enters_intervention_without_creating_task() {
    let tempdir = TempDir::new().expect("create tempdir");
    let root = tempdir.path().join("mentat-root");
    let workspace = tempdir.path().join("workspace");
    fs::create_dir_all(&root).expect("create mentat root");
    fs::create_dir_all(&workspace).expect("create workspace");

    let storage = Storage::from_root(root.clone());
    let session = storage
        .create_session(workspace.clone())
        .expect("create session");
    let controller = SessionController::new(storage.clone());

    let outcome = controller
        .submit_input(
            &session.session_id,
            "Mentat, review this workspace task and supervise the next steps.",
        )
        .expect("submit explicit mentat request");

    assert!(outcome.answer.is_none());
    assert_eq!(outcome.completed_agent_role, None);
    let mentat = outcome.mentat.expect("mentat state");
    assert_eq!(mentat.status, SessionMentatStatus::Intervening);
    assert_eq!(mentat.source, "explicit");
    assert_eq!(
        mentat.target,
        Some(SessionMentatTarget {
            raw_user_request: "Mentat, review this workspace task and supervise the next steps."
                .to_string(),
            goal: "review this workspace task and supervise the next steps".to_string(),
            spec_path: None,
        })
    );

    let updated = storage
        .load_session(&session.session_id)
        .expect("reload session");
    let persisted = updated.mentat.expect("persisted mentat state");
    assert_eq!(persisted.status, SessionMentatStatus::Intervening);
    assert_eq!(persisted.source, "explicit");
    assert_eq!(persisted.target, mentat.target);
}

#[test]
fn direct_mentat_reflection_persists_durable_session_reflection() {
    let tempdir = TempDir::new().expect("create tempdir");
    let root = tempdir.path().join("mentat-root");
    let workspace = tempdir.path().join("workspace");
    fs::create_dir_all(&root).expect("create mentat root");
    fs::create_dir_all(&workspace).expect("create workspace");
    let codex_bin = write_session_fake_codex_bin(tempdir.path());
    let _codex_bin_guard = EnvVarGuard::set("MENTAI_CODEX_BIN", codex_bin.as_os_str());

    let storage = Storage::from_root(root.clone());
    let session = storage
        .create_session(workspace.clone())
        .expect("create session");
    let controller = SessionController::new(storage.clone());

    controller
        .submit_input(&session.session_id, "Mentat, What is Mentat?")
        .expect("enter mentat intervention");

    let raw_event_path = root
        .join("sessions")
        .join(&session.session_id)
        .join("mentat-reflection.ndjson");
    let result = controller
        .run_mentat_reflection_with_cancel_and_raw_event_path(
            &session.session_id,
            None,
            None,
            &raw_event_path,
        )
        .expect("run mentat reflection");
    assert_eq!(result, "Mentat reflection complete");

    let reflections_dir = root
        .join("sessions")
        .join(&session.session_id)
        .join("reflections");
    let reflection_paths = fs::read_dir(&reflections_dir)
        .expect("read reflections dir")
        .map(|entry| entry.expect("reflection entry").path())
        .collect::<Vec<_>>();
    assert_eq!(reflection_paths.len(), 1, "expected one durable reflection");

    let reflection_json: Value = serde_json::from_str(
        &fs::read_to_string(&reflection_paths[0]).expect("read reflection json"),
    )
    .expect("parse reflection json");
    assert_eq!(
        reflection_json.get("session_id").and_then(Value::as_str),
        Some(session.session_id.as_str())
    );
    assert_eq!(
        reflection_json.get("reason").and_then(Value::as_str),
        Some("explicit")
    );
    assert_eq!(
        reflection_json
            .get("goal")
            .and_then(|goal| goal.get("goal"))
            .and_then(Value::as_str),
        Some("What is Mentat?")
    );
    assert_eq!(
        reflection_json.get("content").and_then(Value::as_str),
        Some("Mentat is an AI reflection harness.")
    );
}

#[test]
fn direct_mentat_reflection_can_materialize_durable_session_improvement() {
    let tempdir = TempDir::new().expect("create tempdir");
    let root = tempdir.path().join("mentat-root");
    let workspace = tempdir.path().join("workspace");
    fs::create_dir_all(&root).expect("create mentat root");
    fs::create_dir_all(&workspace).expect("create workspace");
    let codex_bin = write_session_fake_codex_bin(tempdir.path());
    let _codex_bin_guard = EnvVarGuard::set("MENTAI_CODEX_BIN", codex_bin.as_os_str());

    let storage = Storage::from_root(root.clone());
    let session = storage
        .create_session(workspace.clone())
        .expect("create session");
    let controller = SessionController::new(storage.clone());

    controller
        .submit_input(
            &session.session_id,
            "Mentat, review this workspace and improve the reflection workflow.",
        )
        .expect("enter mentat intervention");

    let raw_event_path = root
        .join("sessions")
        .join(&session.session_id)
        .join("mentat-reflection.ndjson");
    controller
        .run_mentat_reflection_with_cancel_and_raw_event_path(
            &session.session_id,
            None,
            None,
            &raw_event_path,
        )
        .expect("run mentat reflection");

    let improvements_dir = root
        .join("sessions")
        .join(&session.session_id)
        .join("improvements");
    let improvement_paths = fs::read_dir(&improvements_dir)
        .expect("read improvements dir")
        .map(|entry| entry.expect("improvement entry").path())
        .collect::<Vec<_>>();
    assert_eq!(
        improvement_paths.len(),
        1,
        "expected one durable improvement"
    );

    let improvement_json: Value = serde_json::from_str(
        &fs::read_to_string(&improvement_paths[0]).expect("read improvement json"),
    )
    .expect("parse improvement json");
    assert_eq!(
        improvement_json.get("session_id").and_then(Value::as_str),
        Some(session.session_id.as_str())
    );
    assert_eq!(
        improvement_json
            .get("goal")
            .and_then(|goal| goal.get("goal"))
            .and_then(Value::as_str),
        Some("review this workspace and improve the reflection workflow")
    );
    assert_eq!(
        improvement_json
            .get("proposed_change")
            .and_then(Value::as_str),
        Some("Add a durable improvement record for direct session reflection.")
    );
    assert_eq!(
        improvement_json
            .get("expected_benefit")
            .and_then(Value::as_str),
        Some("Reflection improvements remain reusable after the transcript scrolls away.")
    );
    assert_eq!(
        improvement_json
            .get("adoption_hint")
            .and_then(Value::as_str),
        Some("Persist the improvement alongside the session reflection record.")
    );
    assert_eq!(
        improvement_json.get("status").and_then(Value::as_str),
        Some("proposed")
    );
}

#[test]
fn begin_auto_self_reflection_creates_visible_self_reflection_state() {
    let tempdir = TempDir::new().expect("create tempdir");
    let root = tempdir.path().join("mentat-root");
    let workspace = tempdir.path().join("workspace");
    fs::create_dir_all(&root).expect("create mentat root");
    fs::create_dir_all(&workspace).expect("create workspace");

    let storage = Storage::from_root(root.clone());
    let session = storage
        .create_session(workspace.clone())
        .expect("create session");
    let controller = SessionController::new(storage);

    let mentat = controller
        .begin_auto_self_reflection(&session.session_id, "What is Mentat?")
        .expect("begin auto self reflection")
        .expect("mentat should start");

    assert_eq!(mentat.source, "auto");
    assert_eq!(mentat.phase, Some(SessionMentatPhase::SelfReflection));
    assert_eq!(
        mentat
            .target
            .as_ref()
            .map(|target| target.raw_user_request.as_str()),
        Some("What is Mentat?")
    );
}

#[test]
fn session_improvement_storage_lists_saved_records() {
    let tempdir = TempDir::new().expect("create tempdir");
    let root = tempdir.path().join("mentat-root");
    let workspace = tempdir.path().join("workspace");
    fs::create_dir_all(&root).expect("create mentat root");
    fs::create_dir_all(&workspace).expect("create workspace");

    let storage = Storage::from_root(root.clone());
    let session = storage
        .create_session(workspace.clone())
        .expect("create session");

    storage
        .save_session_improvement(
            &session.session_id,
            &SessionImprovementRecord {
                improvement_id: "improvement-1".to_string(),
                reflection_id: "reflection-1".to_string(),
                session_id: session.session_id.clone(),
                created_at: "2026-03-13T10:00:00Z".to_string(),
                goal: SessionMentatTarget {
                    raw_user_request: "Review the workflow.".to_string(),
                    goal: "Review the workflow".to_string(),
                    spec_path: None,
                },
                proposed_change: "Add a reusable checklist.".to_string(),
                expected_benefit: "Review output becomes easier to compare.".to_string(),
                adoption_hint: "Create the checklist in the workspace root.".to_string(),
                status: SessionImprovementStatus::Proposed,
            },
        )
        .expect("save session improvement");

    let improvements = storage
        .list_session_improvements(&session.session_id)
        .expect("list session improvements");
    assert_eq!(improvements.len(), 1);
    assert_eq!(improvements[0].improvement_id, "improvement-1");
    assert_eq!(improvements[0].status, SessionImprovementStatus::Proposed);
}

#[test]
fn rejecting_session_improvement_updates_durable_status() {
    let tempdir = TempDir::new().expect("create tempdir");
    let root = tempdir.path().join("mentat-root");
    let workspace = tempdir.path().join("workspace");
    fs::create_dir_all(&root).expect("create mentat root");
    fs::create_dir_all(&workspace).expect("create workspace");

    let storage = Storage::from_root(root.clone());
    let session = storage
        .create_session(workspace.clone())
        .expect("create session");
    storage
        .save_session_improvement(
            &session.session_id,
            &SessionImprovementRecord {
                improvement_id: "improvement-1".to_string(),
                reflection_id: "reflection-1".to_string(),
                session_id: session.session_id.clone(),
                created_at: "2026-03-13T10:00:00Z".to_string(),
                goal: SessionMentatTarget {
                    raw_user_request: "Review the workflow.".to_string(),
                    goal: "Review the workflow".to_string(),
                    spec_path: None,
                },
                proposed_change: "Add a reusable checklist.".to_string(),
                expected_benefit: "Review output becomes easier to compare.".to_string(),
                adoption_hint: "Create the checklist in the workspace root.".to_string(),
                status: SessionImprovementStatus::Proposed,
            },
        )
        .expect("save session improvement");

    let controller = SessionController::new(storage.clone());
    controller
        .reject_session_improvement(&session.session_id, "improvement-1")
        .expect("reject session improvement");

    let improvement = storage
        .load_session_improvement(&session.session_id, "improvement-1")
        .expect("load session improvement");
    assert_eq!(improvement.status, SessionImprovementStatus::Rejected);
}

struct EnvVarGuard {
    key: &'static str,
    previous: Option<OsString>,
}

impl EnvVarGuard {
    fn set(key: &'static str, value: &OsStr) -> Self {
        let previous = std::env::var_os(key);
        unsafe {
            std::env::set_var(key, value);
        }
        Self { key, previous }
    }
}

impl Drop for EnvVarGuard {
    fn drop(&mut self) {
        unsafe {
            match &self.previous {
                Some(value) => std::env::set_var(self.key, value),
                None => std::env::remove_var(self.key),
            }
        }
    }
}

fn write_session_fake_codex_bin(root: &Path) -> std::path::PathBuf {
    let script_path = root.join("session-fake-codex.py");
    fs::write(&script_path, session_fake_codex_script()).expect("write fake codex script");

    let mut permissions = fs::metadata(&script_path)
        .expect("read fake codex metadata")
        .permissions();
    permissions.set_mode(0o755);
    fs::set_permissions(&script_path, permissions).expect("chmod fake codex");

    script_path
}

fn session_fake_codex_script() -> &'static str {
    r###"#!/usr/bin/env python3
import json
import sys

THREAD_ID = "thread-1"
TURN_COUNTER = 0


def send(message):
    sys.stdout.write(json.dumps(message) + "\n")
    sys.stdout.flush()


for raw_line in sys.stdin:
    line = raw_line.strip()
    if not line:
        continue
    message = json.loads(line)
    method = message.get("method")

    if method == "initialize" and "id" in message:
        send({"id": message["id"], "result": {"protocolVersion": "2"}})
        continue

    if method == "initialized":
        continue

    if method == "thread/start" and "id" in message:
        send({"id": message["id"], "result": {"thread": {"id": THREAD_ID}}})
        continue

    if method == "turn/start" and "id" in message:
        TURN_COUNTER += 1
        turn_id = f"turn-{TURN_COUNTER}"
        prompt_parts = []
        for item in message.get("params", {}).get("input", []):
            if item.get("type") == "text":
                prompt_parts.append(item.get("text", ""))
        prompt = "\n".join(prompt_parts)
        if "MENTAT_REFLECTION_REQUEST_JSON:" in prompt and "improve the reflection workflow" in prompt:
            answer = (
                "The current reflection workflow should leave behind a reusable improvement.\n\n"
                "## Improvement\n"
                "- Change: Add a durable improvement record for direct session reflection.\n"
                "- Benefit: Reflection improvements remain reusable after the transcript scrolls away.\n"
                "- Adoption: Persist the improvement alongside the session reflection record."
            )
        else:
            answer = "Mentat is an AI reflection harness." if "What is Mentat?" in prompt else "conversation"
        send({"id": message["id"], "result": {"turn": {"id": turn_id, "status": "running"}}})
        send(
            {
                "method": "item/agentMessage/delta",
                "params": {
                    "delta": answer,
                    "itemId": f"msg-{TURN_COUNTER}",
                    "threadId": THREAD_ID,
                    "turnId": turn_id,
                },
            }
        )
        send(
            {
                "method": "item/completed",
                "params": {
                    "item": {
                        "id": f"msg-{TURN_COUNTER}",
                        "type": "agentMessage",
                        "text": answer,
                    }
                },
            }
        )
        send(
            {
                "method": "turn/completed",
                "params": {"turn": {"id": turn_id, "status": "completed"}},
            }
        )
        continue
"###
}
