use crate::{
    invocation::{SkillContext, SkillInput},
    outcome::SkillOutcome,
    skill::{Skill, SkillId},
};
use forge::budget::estimator::TokenEstimator;
use serde_json::json;
use weave::lenses::{
    clarity::ClarityLens, focus::FocusLens, narrow::NarrowLens, Lens, LensContext,
};

pub struct SieveSkill;

impl Skill for SieveSkill {
    fn id(&self) -> SkillId {
        SkillId("sieve")
    }
    fn display_name(&self) -> &str {
        "Sieve"
    }

    fn input_schema(&self) -> serde_json::Value {
        json!({
            "type": "object",
            "properties": {
                "content": { "type": "string" },
                "task_hint": { "type": "string" },
                "budget_tokens": { "type": "integer", "default": 2000 },
                "source_kind": { "type": "string", "enum": ["file", "shell", "mixed"] }
            },
            "required": ["content"]
        })
    }

    fn output_schema(&self) -> serde_json::Value {
        json!({
            "type": "object",
            "properties": {
                "filtered": { "type": "string" },
                "lines_removed": { "type": "integer" },
                "tokens_before": { "type": "integer" },
                "tokens_after": { "type": "integer" },
                "savings_pct": { "type": "number" }
            }
        })
    }

    fn execute(&self, input: SkillInput, _ctx: &SkillContext) -> anyhow::Result<SkillOutcome> {
        let content = input.get_str("content").unwrap_or("").to_string();
        let task_hint = input.get_str("task_hint").map(|s| s.to_string());
        let budget = input.get_u64("budget_tokens").unwrap_or(2000) as usize;

        let lines_before = content.lines().count();
        let tokens_before = TokenEstimator::count(&content);

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

        let after_clarity = ClarityLens.apply(&content, &lens_ctx);
        let after_focus = FocusLens.apply(&after_clarity.content, &lens_ctx);
        let after_narrow = NarrowLens.apply(&after_focus.content, &lens_ctx);

        let tokens_after = TokenEstimator::count(&after_narrow.content);
        let lines_after = after_narrow.content.lines().count();

        Ok(SkillOutcome::success(
            "sieve",
            json!({
                "filtered": after_narrow.content,
                "lines_removed": lines_before.saturating_sub(lines_after),
                "tokens_before": tokens_before,
                "tokens_after": tokens_after,
                "savings_pct": TokenEstimator::savings_pct(tokens_before, tokens_after)
            }),
        )
        .with_tokens(tokens_after))
    }
}
