use crate::{
    invocation::{SkillContext, SkillInput},
    outcome::SkillOutcome,
    skill::{Skill, SkillId},
    vault_store,
};
use forge::{budget::estimator::TokenEstimator, tracker::store::ExecutionStore};
use serde_json::json;
use vault::fact::MemoryTier;
use vault::retrieval::query::VaultQuery;

pub struct CartridgeSkill;

impl Skill for CartridgeSkill {
    fn id(&self) -> SkillId {
        SkillId("cartridge")
    }
    fn display_name(&self) -> &str {
        "Cartridge"
    }

    fn input_schema(&self) -> serde_json::Value {
        json!({
            "type": "object",
            "properties": {
                "session_id": { "type": "string" },
                "max_tokens": { "type": "integer", "default": 4000 },
                "include_tiers": {
                    "type": "array",
                    "items": { "type": "string", "enum": ["crystallized","resonant","immediate"] },
                    "default": ["crystallized","resonant"]
                },
                "include_recent_commands": { "type": "boolean", "default": true }
            }
        })
    }

    fn output_schema(&self) -> serde_json::Value {
        json!({ "type": "object", "properties": {
            "bundle": { "type": "object" },
            "total_tokens": { "type": "integer" },
            "items_included": { "type": "integer" },
            "items_dropped": { "type": "integer" }
        }})
    }

    fn execute(&self, input: SkillInput, ctx: &SkillContext) -> anyhow::Result<SkillOutcome> {
        let max_tokens = input.get_u64("max_tokens").unwrap_or(4000) as usize;
        let session = input
            .get_str("session_id")
            .unwrap_or(&ctx.session_id)
            .to_string();
        let include_cmds = input.get_bool("include_recent_commands").unwrap_or(true);

        // Determine which tiers to include
        let tier_names: Vec<String> = input
            .params
            .get("include_tiers")
            .and_then(|v| v.as_array())
            .map(|arr| {
                arr.iter()
                    .filter_map(|v| v.as_str().map(|s| s.to_string()))
                    .collect()
            })
            .unwrap_or_else(|| vec!["crystallized".into(), "resonant".into()]);

        let tiers: Vec<MemoryTier> = tier_names
            .iter()
            .filter_map(|t| match t.as_str() {
                "crystallized" => Some(MemoryTier::Crystallized),
                "resonant" => Some(MemoryTier::Resonant),
                "immediate" => Some(MemoryTier::Immediate),
                _ => None,
            })
            .collect();

        // Query vault facts
        let mut vault_q = VaultQuery::new("").top_k(200);
        for tier in tiers {
            vault_q = vault_q.in_tier(tier);
        }
        let facts = vault_store::vault().lock().unwrap().query(&vault_q);

        // Build sections, respecting token budget
        let mut sections: Vec<serde_json::Value> = Vec::new();
        let mut total_tokens = 0usize;
        let mut items_included = 0usize;
        let mut items_dropped = 0usize;

        // Section 1: Vault knowledge
        let mut vault_entries: Vec<serde_json::Value> = Vec::new();
        for fact in &facts {
            let entry = json!({
                "tier": format!("{:?}", fact.tier).to_lowercase(),
                "category": fact.category,
                "key": fact.key,
                "value": fact.value,
                "confidence": fact.confidence
            });
            let entry_tokens = TokenEstimator::count(&entry.to_string());
            if total_tokens + entry_tokens <= max_tokens {
                total_tokens += entry_tokens;
                vault_entries.push(entry);
                items_included += 1;
            } else {
                items_dropped += 1;
            }
        }
        if !vault_entries.is_empty() {
            sections.push(json!({ "kind": "vault", "entries": vault_entries }));
        }

        // Section 2: Recent commands
        if include_cmds {
            let home = std::env::var("HOME").unwrap_or_else(|_| ".".into());
            let db_path = std::path::PathBuf::from(&home)
                .join(".bctx")
                .join("executions.db");
            if let Ok(store) = ExecutionStore::open(&db_path) {
                if let Ok(records) = store.recent(10) {
                    let cmds: Vec<serde_json::Value> = records
                        .iter()
                        .map(|r| {
                            json!({
                                "command": format!("{} {}", r.program, r.args),
                                "exit_code": r.exit_code,
                                "tokens_saved": r.tokens_before.saturating_sub(r.tokens_after)
                            })
                        })
                        .collect();
                    let cmds_tokens =
                        TokenEstimator::count(&serde_json::to_string(&cmds).unwrap_or_default());
                    if total_tokens + cmds_tokens <= max_tokens {
                        total_tokens += cmds_tokens;
                        items_included += cmds.len();
                        sections.push(json!({ "kind": "recent_commands", "entries": cmds }));
                    }
                }
            }
        }

        let manifest = json!({
            "session_id": session,
            "created_at": chrono::Utc::now().to_rfc3339(),
            "max_tokens": max_tokens,
            "tiers_included": tier_names
        });

        Ok(SkillOutcome::success(
            "cartridge",
            json!({
                "bundle": { "manifest": manifest, "sections": sections },
                "total_tokens": total_tokens,
                "items_included": items_included,
                "items_dropped": items_dropped
            }),
        )
        .with_tokens(total_tokens))
    }
}
