//! Planner module
//!
//! The Planner is responsible for:
//! - Understanding user intent
//! - Selecting appropriate actions
//! - Building dependency relationships
//!
//! The Planner does NOT handle:
//! - Parameter expansion details
//! - Parallel/retry strategies
//! - Runtime error recovery

use async_trait::async_trait;
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use serde_json::Value;
use thiserror::Error;

use crate::action::ActionMeta;
use crate::types::{Intent, Plan};

/// Planner single-action output.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SingleAction {
    pub action: String,
    pub params: Value,
    #[serde(default)]
    pub reason: Option<String>,
}

/// Legacy alias preserved while call sites migrate.
pub type ActionCall = SingleAction;

#[derive(Debug, Clone)]
pub enum PlannerOutput {
    /// Execute a single direct action.
    SingleAction(SingleAction),
    /// Execute a small concrete DAG in one planner turn.
    MiniPlan(Plan),
    /// Reply directly w    ithout executing any plan.
    Done(String),
    /// Ask user for missing input and wait for the next turn.
    NeedInput(String),
}

/// Planner errors
#[derive(Debug, Error)]
pub enum PlanError {
    #[error("Failed to understand intent: {0}")]
    IntentUnderstanding(String),

    #[error("No suitable actions found for intent")]
    NoSuitableActions,

    #[error("Failed to generate plan: {0}")]
    Generation(String),

    #[error("LLM error: {0}")]
    LlmError(String),

    #[error("Internal error: {0}")]
    Internal(String),
}

/// Planner trait - generates execution plans from user intent
///
/// Implementations can use different LLM backends or planning strategies.
#[async_trait]
pub trait Planner: Send + Sync {
    /// Generate output from user intent
    async fn plan(
        &self,
        intent: &Intent,
        context: &PlannerContext,
    ) -> Result<PlannerOutput, PlanError>;
}

/// Context provided to the planner
pub struct PlannerContext {
    /// Available actions and their metadata
    pub available_actions: Vec<ActionMeta>,
    /// Conversation/interaction history
    pub history: Vec<HistoryItem>,
    /// Runtime host information for platform-aware planning.
    pub runtime_info: PlannerRuntimeInfo,
    /// Activated skill instructions for this planning turn (keyword-matched).
    pub skill_instructions: Vec<SkillInstruction>,
    /// All available skill names and descriptions for the planner catalog.
    pub skill_summaries: Vec<(String, String)>,
    /// Observed execution state from prior planner iterations in the same turn.
    pub loop_context: Option<PlannerLoopContext>,
}

/// Matched skill instruction attached to planner context.
#[derive(Debug, Clone, Default)]
pub struct SkillInstruction {
    pub skill_name: String,
    pub instructions: String,
    pub skill_path: Option<String>,
    pub scripts_dir: Option<String>,
    /// Skill-local virtual-env python path (e.g. `.claude/skills/xlsx/.venv/bin/python3`).
    pub venv_python: Option<String>,
}

/// Agent-loop execution state surfaced back into the planner.
#[derive(Debug, Clone, Default)]
pub struct PlannerLoopContext {
    pub iteration: usize,
    pub max_iterations: usize,
    pub recent_observations: Vec<String>,
    pub completed_step_ids: Vec<String>,
    pub available_bindings: Vec<String>,
    pub binding_shapes: Vec<String>,
    pub working_set_preview: Option<String>,
}

impl PlannerContext {
    /// Create a new planner context
    pub fn new(available_actions: Vec<ActionMeta>) -> Self {
        Self {
            available_actions,
            history: Vec::new(),
            runtime_info: PlannerRuntimeInfo::default(),
            skill_instructions: Vec::new(),
            skill_summaries: Vec::new(),
            loop_context: None,
        }
    }

    /// Create context with history
    pub fn with_history(available_actions: Vec<ActionMeta>, history: Vec<HistoryItem>) -> Self {
        Self {
            available_actions,
            history,
            runtime_info: PlannerRuntimeInfo::default(),
            skill_instructions: Vec::new(),
            skill_summaries: Vec::new(),
            loop_context: None,
        }
    }

    /// Attach runtime host information.
    pub fn with_runtime_info(mut self, runtime_info: PlannerRuntimeInfo) -> Self {
        self.runtime_info = runtime_info;
        self
    }

    /// Attach matched skill instructions.
    pub fn with_skill_instructions(mut self, skills: Vec<SkillInstruction>) -> Self {
        self.skill_instructions = skills;
        self
    }

    /// Attach all skill summaries for the planner catalog.
    pub fn with_skill_summaries(mut self, summaries: Vec<(String, String)>) -> Self {
        self.skill_summaries = summaries;
        self
    }

    /// Attach agent-loop execution context from earlier iterations.
    pub fn with_loop_context(mut self, loop_context: PlannerLoopContext) -> Self {
        self.loop_context = Some(loop_context);
        self
    }

    /// Add a history item
    pub fn add_history(&mut self, item: HistoryItem) {
        self.history.push(item);
    }

    /// Get action by name
    pub fn get_action(&self, name: &str) -> Option<&ActionMeta> {
        self.available_actions.iter().find(|a| a.name == name)
    }
}

/// Runtime host info visible to planner prompt.
#[derive(Debug, Clone, Default)]
pub struct PlannerRuntimeInfo {
    pub os: String,
    pub os_family: String,
    pub arch: String,
    pub shell: Option<String>,
}

impl PlannerRuntimeInfo {
    pub fn detect() -> Self {
        Self {
            os: std::env::consts::OS.to_string(),
            os_family: std::env::consts::FAMILY.to_string(),
            arch: std::env::consts::ARCH.to_string(),
            shell: std::env::var("SHELL").ok(),
        }
    }
}

/// A single item in the conversation history
#[derive(Debug, Clone)]
pub struct HistoryItem {
    /// Role (e.g., "user", "assistant", "system")
    pub role: String,
    /// Content of the message
    pub content: String,
    /// Timestamp
    pub timestamp: DateTime<Utc>,
}

impl HistoryItem {
    /// Create a new history item
    pub fn new(role: impl Into<String>, content: impl Into<String>) -> Self {
        Self {
            role: role.into(),
            content: content.into(),
            timestamp: Utc::now(),
        }
    }

    /// Create a user message
    pub fn user(content: impl Into<String>) -> Self {
        Self::new("user", content)
    }

    /// Create an assistant message
    pub fn assistant(content: impl Into<String>) -> Self {
        Self::new("assistant", content)
    }

    /// Create a system message
    pub fn system(content: impl Into<String>) -> Self {
        Self::new("system", content)
    }
}
