use crate::{
    invocation::{SkillContext, SkillInput},
    outcome::SkillOutcome,
    skill::{Skill, SkillId},
    vault_store,
};
use serde_json::json;
use vault::fact::MemoryTier;
use vault::retrieval::query::VaultQuery;

pub struct ArchivistSkill;

impl Skill for ArchivistSkill {
    fn id(&self) -> SkillId {
        SkillId("archivist")
    }
    fn display_name(&self) -> &str {
        "Archivist"
    }

    fn input_schema(&self) -> serde_json::Value {
        json!({
            "type": "object",
            "properties": {
                "query":          { "type": "string" },
                "top_k":          { "type": "integer", "default": 10 },
                "min_confidence": { "type": "number",  "default": 0.0 },
                "tiers": {
                    "type": "array",
                    "items": { "type": "string", "enum": ["immediate","resonant","crystallized","dormant"] }
                }
            },
            "required": ["query"]
        })
    }

    fn output_schema(&self) -> serde_json::Value {
        json!({
            "type": "object",
            "properties": {
                "hits":          { "type": "array" },
                "tiers_queried": { "type": "array" },
                "total_found":   { "type": "integer" }
            }
        })
    }

    fn execute(&self, input: SkillInput, _ctx: &SkillContext) -> anyhow::Result<SkillOutcome> {
        let keyword = input.get_str("query").unwrap_or("").to_string();
        let top_k = input.get_u64("top_k").unwrap_or(10) as usize;
        let min_confidence = input
            .params
            .get("min_confidence")
            .and_then(|v| v.as_f64())
            .unwrap_or(0.0) as f32;

        // Parse optional tier filter
        let tier_filter: Vec<MemoryTier> = input
            .params
            .get("tiers")
            .and_then(|v| v.as_array())
            .map(|arr| {
                arr.iter()
                    .filter_map(|t| t.as_str())
                    .filter_map(str_to_tier)
                    .collect()
            })
            .unwrap_or_default();

        let mut q = VaultQuery::new(&keyword).top_k(top_k);
        if min_confidence > 0.0 {
            q = q.min_confidence(min_confidence);
        }
        for tier in &tier_filter {
            q = q.in_tier(tier.clone());
        }

        let vault = vault_store::vault().lock().unwrap();
        let facts = vault.query(&q);

        let tiers_queried: Vec<&str> = if tier_filter.is_empty() {
            vec!["immediate", "resonant", "crystallized", "dormant"]
        } else {
            tier_filter.iter().map(tier_to_str).collect()
        };

        let hits: Vec<serde_json::Value> = facts
            .iter()
            .map(|f| {
                json!({
                    "key":        f.key,
                    "value":      f.value,
                    "category":   f.category,
                    "confidence": f.confidence,
                    "tier":       tier_to_str(&f.tier),
                    "tags":       f.tags,
                    "age_days":   f.age_days(),
                })
            })
            .collect();

        let total = hits.len();
        Ok(SkillOutcome::success(
            "archivist",
            json!({
                "hits":          hits,
                "tiers_queried": tiers_queried,
                "total_found":   total,
            }),
        ))
    }
}

fn str_to_tier(s: &str) -> Option<MemoryTier> {
    match s {
        "immediate" => Some(MemoryTier::Immediate),
        "resonant" => Some(MemoryTier::Resonant),
        "crystallized" => Some(MemoryTier::Crystallized),
        "dormant" => Some(MemoryTier::Dormant),
        _ => None,
    }
}

fn tier_to_str(t: &MemoryTier) -> &'static str {
    match t {
        MemoryTier::Immediate => "immediate",
        MemoryTier::Resonant => "resonant",
        MemoryTier::Crystallized => "crystallized",
        MemoryTier::Dormant => "dormant",
        MemoryTier::Phantom => "phantom",
    }
}
