use crate::{
    invocation::{SkillContext, SkillInput},
    outcome::SkillOutcome,
    skill::{Skill, SkillId},
};
use serde_json::json;
use weave::lenses::{
    apply_stack, clarity::ClarityLens, focus::FocusLens, harmonic::HarmonicLens,
    narrow::NarrowLens, refract::RefractLens, Lens, LensContext,
};

pub struct CondenserSkill;

impl Skill for CondenserSkill {
    fn id(&self) -> SkillId {
        SkillId("condenser")
    }
    fn display_name(&self) -> &str {
        "Condenser"
    }

    fn input_schema(&self) -> serde_json::Value {
        json!({
            "type": "object",
            "properties": {
                "content": { "type": "string" },
                "path": { "type": "string" },
                "lens": {
                    "type": "string",
                    "enum": ["clarity", "focus", "narrow", "refract", "harmonic", "auto"],
                    "default": "auto"
                },
                "task_hint": { "type": "string" },
                "budget_tokens": { "type": "integer", "default": 2000 }
            }
        })
    }

    fn output_schema(&self) -> serde_json::Value {
        json!({ "type": "object", "properties": {
            "condensed":       { "type": "string" },
            "original_tokens": { "type": "integer" },
            "result_tokens":   { "type": "integer" },
            "savings_pct":     { "type": "number" },
            "lens_applied":    { "type": "string" }
        }})
    }

    fn execute(&self, input: SkillInput, _ctx: &SkillContext) -> anyhow::Result<SkillOutcome> {
        let content = if let Some(c) = input.get_str("content") {
            c.to_string()
        } else if let Some(p) = input.get_str("path") {
            std::fs::read_to_string(p).unwrap_or_default()
        } else {
            return Err(anyhow::anyhow!("either content or path is required"));
        };

        let budget = input.get_u64("budget_tokens").unwrap_or(2000) as usize;
        let task_hint = input.get_str("task_hint").map(|s| s.to_string());

        let mut lens_ctx = LensContext::new(budget);
        if let Some(hint) = task_hint {
            lens_ctx = lens_ctx.with_task(hint);
        }

        let lens_name = input.get_str("lens").unwrap_or("auto");
        let lenses: Vec<Box<dyn Lens>> = match lens_name {
            "clarity" => vec![Box::new(ClarityLens)],
            "focus" => vec![Box::new(ClarityLens), Box::new(FocusLens)],
            "narrow" => vec![Box::new(ClarityLens), Box::new(NarrowLens)],
            "refract" => vec![Box::new(ClarityLens), Box::new(RefractLens)],
            "harmonic" => vec![Box::new(ClarityLens), Box::new(HarmonicLens)],
            _ => vec![
                Box::new(ClarityLens),
                Box::new(FocusLens),
                Box::new(NarrowLens),
            ],
        };

        let result = apply_stack(&lenses, &content, &lens_ctx);

        Ok(SkillOutcome::success(
            "condenser",
            json!({
                "condensed":       result.content,
                "original_tokens": result.tokens_before,
                "result_tokens":   result.tokens_after,
                "savings_pct":     result.savings_pct(),
                "lens_applied":    lens_name
            }),
        )
        .with_tokens(result.tokens_after))
    }
}
