use std::path::Path;
use std::sync::Arc;
use std::env;
use anyhow::{Result, Context, anyhow};
use serde::{Serialize, Deserialize};
use serde::de::DeserializeOwned;
use globset::Glob;
use regex::Regex;
use crate::{DocOS, DocManager, Vgcs, DocNodeKind};

// --- Data Structures ---

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum OutputFormat {
    Text,
    Json,
}

#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
pub struct NodeMetadata {
    pub path: String,
    pub kind: String, // "File" or "Dir"
    pub size: u64,
    // pub modified: bool, // TODO: Implement diff check against base
}

#[derive(Debug, Default, Clone)]
pub struct FindOptions {
    pub recursive: bool,
    pub max_depth: Option<usize>,
    pub type_filter: Option<String>, // "File" or "Dir"
    pub min_size: Option<u64>,
    pub max_size: Option<u64>,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct GrepMatch {
    pub path: String,
    pub line_number: usize,
    pub content: String,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct FileStats {
    pub path: String,
    pub lines: usize,
    pub bytes: usize,
}

// --- Trait Definition ---

pub trait ContextShell {
    fn tree(&self, path: &str, depth: Option<usize>, format: OutputFormat) -> Result<String>;
    fn find(&self, name_pattern: &str, options: FindOptions) -> Result<Vec<NodeMetadata>>;
    fn grep(&self, pattern: &str, paths: Option<Vec<String>>) -> Result<Vec<GrepMatch>>;
    fn cat(&self, paths: &[String]) -> Result<String>;
    fn wc(&self, paths: &[String]) -> Result<Vec<FileStats>>;
    fn patch(&mut self, path: &str, original: &str, replacement: &str) -> Result<()>;
}

// --- WorkerContext Implementation ---

pub struct WorkerContext {
    doc: DocOS<Vgcs>,
}

impl WorkerContext {
    pub fn from_env() -> Result<Self> {
        let req_id = env::var("WORKFLOW_REQ_ID").context("Missing WORKFLOW_REQ_ID")?;
        let commit = env::var("WORKFLOW_BASE_COMMIT").context("Missing WORKFLOW_BASE_COMMIT")?;
        let data_path = env::var("WORKFLOW_DATA_PATH").context("Missing WORKFLOW_DATA_PATH")?;
        
        let vgcs = Vgcs::new(&data_path);
        let doc = DocOS::new(Arc::new(vgcs), &req_id, &commit);
        
        Ok(Self { doc })
    }

    pub fn new(data_path: &str, req_id: &str, commit: &str) -> Self {
        let vgcs = Vgcs::new(data_path);
        let doc = DocOS::new(Arc::new(vgcs), req_id, commit);
        Self { doc }
    }

    pub fn read_json<T: DeserializeOwned>(&self, path: impl AsRef<Path>) -> Result<T> {
        let path_str = path.as_ref().to_string_lossy();
        let content = self.doc.read_content(&path_str)?;
        let data = serde_json::from_str(&content)
            .with_context(|| format!("Failed to parse JSON from {}", path_str))?;
        Ok(data)
    }

    pub fn read_text(&self, path: impl AsRef<Path>) -> Result<String> {
        let path_str = path.as_ref().to_string_lossy();
        self.doc.read_content(&path_str)
    }

    pub fn write_file(&mut self, path: impl AsRef<Path>, content: &str) -> Result<()> {
         let path_str = path.as_ref().to_string_lossy();
         self.doc.write_content(&path_str, content)
    }
    
    pub fn attach_subsection(&mut self, parent: impl AsRef<Path>, name: &str, content: &str) -> Result<()> {
        let parent_str = parent.as_ref().to_string_lossy();
        self.doc.insert_subsection(&parent_str, name, content)
    }

    pub fn commit(&mut self, message: &str) -> Result<String> {
        self.doc.save(message)
    }

    pub fn get_tool_definitions() -> serde_json::Value {
        serde_json::json!([
            {
                "type": "function",
                "function": {
                    "name": "tree",
                    "description": "List directory structure to understand the file layout.",
                    "parameters": {
                        "type": "object",
                        "properties": {
                            "path": { "type": "string", "description": "Root path to list (default: root)" },
                            "depth": { "type": "integer", "description": "Recursion depth" },
                            "format": { "type": "string", "enum": ["Text", "Json"], "default": "Text" }
                        }
                    }
                }
            },
            {
                "type": "function",
                "function": {
                    "name": "find",
                    "description": "Find files by name pattern (glob). Fast metadata search.",
                    "parameters": {
                        "type": "object",
                        "required": ["pattern"],
                        "properties": {
                            "pattern": { "type": "string", "description": "Glob pattern (e.g. **/*.rs)" },
                            "recursive": { "type": "boolean", "default": true }
                        }
                    }
                }
            },
             {
                "type": "function",
                "function": {
                    "name": "grep",
                    "description": "Search for content within files using regex.",
                    "parameters": {
                        "type": "object",
                        "required": ["pattern"],
                        "properties": {
                            "pattern": { "type": "string", "description": "Regex pattern" },
                            "paths": { "type": "array", "items": { "type": "string" }, "description": "Limit search to these paths" }
                        }
                    }
                }
            },
            {
                "type": "function",
                "function": {
                    "name": "cat",
                    "description": "Read and assemble content of multiple files.",
                    "parameters": {
                        "type": "object",
                        "required": ["paths"],
                        "properties": {
                            "paths": { "type": "array", "items": { "type": "string" } }
                        }
                    }
                }
            },
            {
                "type": "function",
                "function": {
                    "name": "patch",
                    "description": "Replace a specific text block in a file. Use this for small corrections.",
                    "parameters": {
                        "type": "object",
                        "required": ["path", "original", "replacement"],
                        "properties": {
                            "path": { "type": "string" },
                            "original": { "type": "string", "description": "Exact text to look for. Must be unique in file." },
                            "replacement": { "type": "string", "description": "New text to insert." }
                        }
                    }
                }
            }
        ])
    }
}

impl ContextShell for WorkerContext {
    fn tree(&self, path: &str, depth: Option<usize>, format: OutputFormat) -> Result<String> {
        let root_node = self.doc.get_outline()?; 
        
        let target_node = if path == "/" || path == "." {
             Some(&root_node)
        } else {
             fn find_node<'a>(node: &'a crate::DocNode, path: &str) -> Option<&'a crate::DocNode> {
                 if node.path == path {
                     return Some(node);
                 }
                 for child in &node.children {
                     if let Some(found) = find_node(child, path) {
                         return Some(found);
                     }
                 }
                 None
             }
             find_node(&root_node, path)
        };
        
        let node = target_node.ok_or_else(|| anyhow!("Path not found: {}", path))?;

        match format {
            OutputFormat::Json => {
                Ok(serde_json::to_string_pretty(node)?)
            },
            OutputFormat::Text => {
                let mut output = String::new();
                fn print_tree(node: &crate::DocNode, prefix: &str, is_last: bool, depth: usize, max_depth: Option<usize>, output: &mut String) {
                     if let Some(max) = max_depth {
                         if depth > max { return; }
                     }
                     
                     let name = if node.path == "/" { "." } else { &node.name };
                     
                     if depth > 0 {
                        let connector = if is_last { "└── " } else { "├── " };
                        output.push_str(&format!("{}{}{}\n", prefix, connector, name));
                     } else {
                        output.push_str(&format!("{}\n", name));
                     }
                     
                     let child_prefix = if depth > 0 {
                         if is_last { format!("{}    ", prefix) } else { format!("{}│   ", prefix) }
                     } else {
                         "".to_string()
                     };

                     for (i, child) in node.children.iter().enumerate() {
                         print_tree(child, &child_prefix, i == node.children.len() - 1, depth + 1, max_depth, output);
                     }
                }
                print_tree(node, "", true, 0, depth, &mut output);
                Ok(output)
            }
        }
    }

    fn find(&self, name_pattern: &str, options: FindOptions) -> Result<Vec<NodeMetadata>> {
        let root = self.doc.get_outline()?;
        let mut results = Vec::new();
        
        let glob = Glob::new(name_pattern)?.compile_matcher();
        
        fn traverse(node: &crate::DocNode, glob: &globset::GlobMatcher, opts: &FindOptions, depth: usize, results: &mut Vec<NodeMetadata>) {
            if let Some(max) = opts.max_depth {
                if depth > max { return; }
            }
            
            let match_name = glob.is_match(&node.name) || glob.is_match(&node.path);
            
            let kind_str = match node.kind {
                DocNodeKind::Composite => "Dir",
                DocNodeKind::Leaf => "File",
                DocNodeKind::Section => "Section",
            };

            let type_match = match &opts.type_filter {
                Some(t) => t.eq_ignore_ascii_case(kind_str),
                None => true,
            };
            
            if depth > 0 && match_name && type_match {
                 results.push(NodeMetadata {
                     path: node.path.clone(),
                     kind: kind_str.to_string(),
                     size: 0, 
                 });
            }
            
            if opts.recursive || depth == 0 {
                 for child in &node.children {
                     traverse(child, glob, opts, depth + 1, results);
                 }
            }
        }
        
        traverse(&root, &glob, &options, 0, &mut results);
        Ok(results)
    }

    fn grep(&self, pattern: &str, paths: Option<Vec<String>>) -> Result<Vec<GrepMatch>> {
        let re = Regex::new(pattern).context("Invalid regex pattern")?;
        
        let target_paths = match paths {
            Some(p) => p,
            None => {
                let all_nodes = self.find("**/*", FindOptions { 
                    recursive: true, 
                    type_filter: Some("File".to_string()), 
                    ..Default::default() 
                })?;
                all_nodes.into_iter().map(|n| n.path).collect()
            }
        };

        let mut matches = Vec::new();

        for path in target_paths {
            if let Ok(content) = self.read_text(&path) {
                for (i, line) in content.lines().enumerate() {
                    if re.is_match(line) {
                        matches.push(GrepMatch {
                            path: path.clone(),
                            line_number: i + 1,
                            content: line.trim().to_string(),
                        });
                    }
                }
            }
        }
        
        Ok(matches)
    }

    fn cat(&self, paths: &[String]) -> Result<String> {
        let mut output = String::new();
        for path in paths {
            match self.read_text(path) {
                Ok(content) => {
                    output.push_str(&format!("<file path=\"{}\">\n", path));
                    output.push_str(&content);
                    if !content.ends_with('\n') {
                        output.push('\n');
                    }
                    output.push_str("</file>\n\n");
                },
                Err(e) => {
                    output.push_str(&format!("<!-- Failed to read {}: {} -->\n", path, e));
                }
            }
        }
        Ok(output)
    }

    fn wc(&self, paths: &[String]) -> Result<Vec<FileStats>> {
        let mut stats = Vec::new();
        for path in paths {
            if let Ok(content) = self.read_text(path) {
                stats.push(FileStats {
                    path: path.clone(),
                    lines: content.lines().count(),
                    bytes: content.len(),
                });
            }
        }
        Ok(stats)
    }

    fn patch(&mut self, path: &str, original: &str, replacement: &str) -> Result<()> {
        let content = self.read_text(path)?;
        
        let matches: Vec<_> = content.match_indices(original).collect();
        
        match matches.len() {
            0 => return Err(anyhow!("Original text not found in {}", path)),
            1 => {
                let new_content = content.replace(original, replacement);
                self.write_file(path, &new_content)?;
                Ok(())
            },
            _ => return Err(anyhow!("Ambiguous match: original text found {} times", matches.len())),
        }
    }
}
