use crate::{
    invocation::{SkillContext, SkillInput},
    outcome::SkillOutcome,
    skill::{Skill, SkillId},
};
use forge::budget::estimator::TokenEstimator;
use serde_json::json;

/// Unfold: reverse a prior Condenser compression — restore a condensed block to its original.
///
/// Works by storing and retrieving from the local expansion registry (~/.bctx/expansions/).
/// If the original is not found, returns the condensed block as-is with a note.
pub struct UnfoldSkill;

impl Skill for UnfoldSkill {
    fn id(&self) -> SkillId {
        SkillId("unfold")
    }
    fn display_name(&self) -> &str {
        "Unfold"
    }

    fn input_schema(&self) -> serde_json::Value {
        json!({
            "type": "object",
            "properties": {
                "condensed": { "type": "string", "description": "Previously compressed content to restore" },
                "path": { "type": "string", "description": "Original file path (used as cache key if provided)" }
            },
            "required": ["condensed"]
        })
    }

    fn output_schema(&self) -> serde_json::Value {
        json!({
            "type": "object",
            "properties": {
                "content": { "type": "string" },
                "restored": { "type": "boolean" },
                "tokens": { "type": "integer" }
            }
        })
    }

    fn execute(&self, input: SkillInput, _ctx: &SkillContext) -> anyhow::Result<SkillOutcome> {
        let condensed = input.get_str("condensed").unwrap_or("").to_string();
        let path = input.get_str("path").map(|s| s.to_string());

        // Try to restore from the original file if path is provided
        if let Some(p) = &path {
            if let Ok(original) = std::fs::read_to_string(p) {
                let tokens = TokenEstimator::count(&original);
                return Ok(SkillOutcome::success(
                    "unfold",
                    json!({
                        "content": original,
                        "restored": true,
                        "tokens": tokens
                    }),
                )
                .with_tokens(tokens));
            }
        }

        // Try expansion registry
        if let Some(original) = lookup_expansion(&condensed) {
            let tokens = TokenEstimator::count(&original);
            return Ok(SkillOutcome::success(
                "unfold",
                json!({
                    "content": original,
                    "restored": true,
                    "tokens": tokens
                }),
            )
            .with_tokens(tokens));
        }

        // Not found — return as-is
        let tokens = TokenEstimator::count(&condensed);
        Ok(SkillOutcome::success(
            "unfold",
            json!({
                "content": condensed,
                "restored": false,
                "tokens": tokens
            }),
        )
        .with_tokens(tokens))
    }
}

fn lookup_expansion(condensed: &str) -> Option<String> {
    use std::collections::hash_map::DefaultHasher;
    use std::hash::{Hash, Hasher};

    let mut h = DefaultHasher::new();
    condensed.hash(&mut h);
    let key = format!("{:x}", h.finish());

    let cache_path = std::path::PathBuf::from(std::env::var("HOME").ok()?)
        .join(".bctx")
        .join("expansions")
        .join(&key);

    std::fs::read_to_string(cache_path).ok()
}
