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

/// Drift: return only the lines that changed since a given git ref, token-budgeted.
pub struct DriftSkill;

impl Skill for DriftSkill {
    fn id(&self) -> SkillId {
        SkillId("drift")
    }
    fn display_name(&self) -> &str {
        "Drift"
    }

    fn input_schema(&self) -> serde_json::Value {
        json!({
            "type": "object",
            "properties": {
                "path": { "type": "string", "description": "File to diff (optional — omit for full diff)" },
                "ref": { "type": "string", "default": "HEAD", "description": "Git ref to compare against" },
                "budget_tokens": { "type": "integer", "default": 3000 },
                "working_dir": { "type": "string", "description": "Git repo root (defaults to CWD)" }
            }
        })
    }

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

    fn execute(&self, input: SkillInput, _ctx: &SkillContext) -> anyhow::Result<SkillOutcome> {
        let git_ref = input.get_str("ref").unwrap_or("HEAD").to_string();
        let path = input.get_str("path").map(|s| s.to_string());
        let budget = input.get_u64("budget_tokens").unwrap_or(3000) as usize;
        let working_dir = input
            .get_str("working_dir")
            .map(std::path::PathBuf::from)
            .unwrap_or_else(|| std::env::current_dir().unwrap_or_default());

        let mut cmd = std::process::Command::new("git");
        cmd.arg("diff").arg(&git_ref);
        if let Some(p) = &path {
            cmd.arg("--").arg(p);
        }
        cmd.current_dir(&working_dir);

        let out = cmd.output()?;
        let raw = String::from_utf8_lossy(&out.stdout).to_string();

        let files_changed = raw.lines().filter(|l| l.starts_with("diff --git")).count();

        let tokens_before = TokenEstimator::count(&raw);
        let ctx = LensContext::new(budget);
        let after_clarity = ClarityLens.apply(&raw, &ctx);
        let after_narrow = NarrowLens.apply(&after_clarity.content, &ctx);
        let tokens_after = TokenEstimator::count(&after_narrow.content);

        Ok(SkillOutcome::success(
            "drift",
            json!({
                "diff": after_narrow.content,
                "files_changed": files_changed,
                "tokens_before": tokens_before,
                "tokens_after": tokens_after
            }),
        )
        .with_tokens(tokens_after))
    }
}
