use super::escape::{escape_command_name, escape_shell_string, escape_variable_name};
use crate::ir::{Command, ShellIR, ShellValue};
use crate::models::{Config, Result};
use std::fmt::Write;

pub struct PosixEmitter {
    #[allow(dead_code)]
    config: Config,
}

impl PosixEmitter {
    pub fn new(config: Config) -> Self {
        Self { config }
    }

    pub fn emit(&self, ir: &ShellIR) -> Result<String> {
        let mut output = String::new();

        // Write the POSIX shell header (without main wrapper yet)
        self.write_header_without_main(&mut output)?;

        // Separate helper functions from main body
        let (helper_functions, main_body) = self.separate_functions(ir);

        // Emit helper functions at global scope
        for func_ir in helper_functions {
            self.emit_ir(&mut output, &func_ir, 0)?;
            writeln!(&mut output)?;
        }

        // Now open main() and emit its body
        writeln!(&mut output, "# Main script begins")?;
        writeln!(&mut output, "main() {{")?;

        if main_body.is_empty() {
            // Empty main needs a no-op for valid shell syntax
            writeln!(&mut output, "    :")?;
        } else {
            for stmt_ir in main_body {
                self.emit_ir(&mut output, &stmt_ir, 1)?;
            }
        }

        // Write the footer (closes main and adds execution)
        self.write_footer(&mut output)?;

        Ok(output)
    }

    fn separate_functions(&self, ir: &ShellIR) -> (Vec<ShellIR>, Vec<ShellIR>) {
        let mut functions = Vec::new();
        let mut main_body = Vec::new();

        if let ShellIR::Sequence(items) = ir {
            for item in items {
                match item {
                    ShellIR::Function { .. } => functions.push(item.clone()),
                    _ => main_body.push(item.clone()),
                }
            }
        } else {
            // If not a sequence, treat as main body
            main_body.push(ir.clone());
        }

        (functions, main_body)
    }

    fn write_header_without_main(&self, output: &mut String) -> Result<()> {
        writeln!(output, "#!/bin/sh")?;
        writeln!(output, "# Generated by Rash v{}", env!("CARGO_PKG_VERSION"))?;
        writeln!(output, "# POSIX-compliant shell script")?;
        writeln!(output)?;

        // Set strict error handling
        writeln!(output, "set -euf")?;
        writeln!(output, "IFS=' \t\n'")?; // POSIX-compatible IFS setting
        writeln!(output, "export LC_ALL=C")?;
        writeln!(output)?;

        // Include runtime functions if needed
        if self.needs_runtime() {
            self.write_runtime(output)?;
        }

        Ok(())
    }

    fn write_footer(&self, output: &mut String) -> Result<()> {
        writeln!(output, "}}")?;
        writeln!(output)?;
        writeln!(output, "# Cleanup on exit")?;
        writeln!(output, "trap 'rm -rf \"${{TMPDIR:-/tmp}}/rash.$$\"' EXIT")?;
        writeln!(output)?;
        writeln!(output, "# Execute main function")?;
        writeln!(output, "main \"$@\"")?;

        Ok(())
    }

    fn write_runtime(&self, output: &mut String) -> Result<()> {
        writeln!(output, "# Rash runtime functions")?;
        self.write_println_function(output)?;
        self.write_require_function(output)?;
        self.write_download_function(output)?;

        // Stdlib functions
        writeln!(output, "# Rash stdlib functions")?;
        self.write_string_trim_function(output)?;
        self.write_string_contains_function(output)?;
        self.write_string_len_function(output)?;
        self.write_string_replace_function(output)?;
        self.write_string_to_upper_function(output)?;
        self.write_string_to_lower_function(output)?;
        self.write_fs_exists_function(output)?;
        self.write_fs_read_file_function(output)?;
        self.write_fs_write_file_function(output)?;
        self.write_fs_copy_function(output)?;
        self.write_fs_remove_function(output)?;
        self.write_fs_is_file_function(output)?;
        self.write_fs_is_dir_function(output)?;
        // Sprint 28: Complete Missing Stdlib Functions - GREEN PHASE
        self.write_string_split_function(output)?;
        self.write_array_len_function(output)?;
        self.write_array_join_function(output)?;

        Ok(())
    }

    fn write_println_function(&self, output: &mut String) -> Result<()> {
        let lines = ["rash_println() {", "    printf '%s\\n' \"$1\"", "}", ""];
        self.write_shell_lines(output, &lines)
    }

    fn write_require_function(&self, output: &mut String) -> Result<()> {
        let lines = [
            "rash_require() {",
            "    if ! \"$@\"; then",
            "        echo \"FATAL: Requirement failed: $*\" >&2",
            "        exit 1",
            "    fi",
            "}",
            "",
        ];
        self.write_shell_lines(output, &lines)
    }

    fn write_download_function(&self, output: &mut String) -> Result<()> {
        writeln!(output, "rash_download_verified() {{")?;
        writeln!(output, "    url=\"$1\"; dst=\"$2\"; checksum=\"$3\"")?;
        writeln!(output, "    ")?;

        self.write_download_logic(output)?;
        self.write_checksum_logic(output)?;

        writeln!(output, "}}")?;
        writeln!(output)?;
        Ok(())
    }

    fn write_download_logic(&self, output: &mut String) -> Result<()> {
        let lines = [
            "    if command -v curl >/dev/null 2>&1; then",
            "        curl -fsSL --proto '=https' --tlsv1.2 \"$url\" -o \"$dst\"",
            "    elif command -v wget >/dev/null 2>&1; then",
            "        wget -qO \"$dst\" \"$url\"",
            "    else",
            "        echo \"FATAL: Neither curl nor wget found\" >&2",
            "        return 1",
            "    fi",
            "    ",
        ];
        self.write_shell_lines(output, &lines)
    }

    fn write_checksum_logic(&self, output: &mut String) -> Result<()> {
        let lines = [
            "    if command -v sha256sum >/dev/null 2>&1; then",
            "        echo \"$checksum  $dst\" | sha256sum -c >/dev/null",
            "    elif command -v shasum >/dev/null 2>&1; then",
            "        echo \"$checksum  $dst\" | shasum -a 256 -c >/dev/null",
            "    else",
            "        echo \"FATAL: No checksum utility found\" >&2",
            "        return 1",
            "    fi",
        ];
        self.write_shell_lines(output, &lines)
    }

    fn write_shell_lines(&self, output: &mut String, lines: &[&str]) -> Result<()> {
        for line in lines {
            writeln!(output, "{line}")?;
        }
        Ok(())
    }

    // Stdlib function implementations

    fn write_string_trim_function(&self, output: &mut String) -> Result<()> {
        let lines = [
            "rash_string_trim() {",
            "    s=\"$1\"",
            "    # Remove leading whitespace",
            "    s=\"${s#\"${s%%[![:space:]]*}\"}\"",
            "    # Remove trailing whitespace",
            "    s=\"${s%\"${s##*[![:space:]]}\"}\"",
            "    printf '%s' \"$s\"",
            "}",
            "",
        ];
        self.write_shell_lines(output, &lines)
    }

    fn write_string_contains_function(&self, output: &mut String) -> Result<()> {
        let lines = [
            "rash_string_contains() {",
            "    haystack=\"$1\"",
            "    needle=\"$2\"",
            "    case \"$haystack\" in",
            "        *\"$needle\"*) return 0 ;;",
            "        *) return 1 ;;",
            "    esac",
            "}",
            "",
        ];
        self.write_shell_lines(output, &lines)
    }

    fn write_string_len_function(&self, output: &mut String) -> Result<()> {
        let lines = [
            "rash_string_len() {",
            "    s=\"$1\"",
            "    printf '%s' \"$s\" | wc -c | tr -d ' '",
            "}",
            "",
        ];
        self.write_shell_lines(output, &lines)
    }

    fn write_fs_exists_function(&self, output: &mut String) -> Result<()> {
        let lines = [
            "rash_fs_exists() {",
            "    path=\"$1\"",
            "    test -e \"$path\"",
            "}",
            "",
        ];
        self.write_shell_lines(output, &lines)
    }

    fn write_fs_read_file_function(&self, output: &mut String) -> Result<()> {
        let lines = [
            "rash_fs_read_file() {",
            "    path=\"$1\"",
            "    if [ ! -f \"$path\" ]; then",
            "        echo \"ERROR: File not found: $path\" >&2",
            "        return 1",
            "    fi",
            "    cat \"$path\"",
            "}",
            "",
        ];
        self.write_shell_lines(output, &lines)
    }

    fn write_fs_write_file_function(&self, output: &mut String) -> Result<()> {
        let lines = [
            "rash_fs_write_file() {",
            "    path=\"$1\"",
            "    content=\"$2\"",
            "    printf '%s' \"$content\" > \"$path\"",
            "}",
            "",
        ];
        self.write_shell_lines(output, &lines)
    }

    fn write_string_replace_function(&self, output: &mut String) -> Result<()> {
        let lines = [
            "rash_string_replace() {",
            "    s=\"$1\"",
            "    old=\"$2\"",
            "    new=\"$3\"",
            "    # POSIX-compliant string replacement using case/sed fallback",
            "    if [ -z \"$old\" ]; then",
            "        printf '%s' \"$s\"",
            "        return",
            "    fi",
            "    # Replace first occurrence using parameter expansion",
            "    printf '%s' \"${s%%\"$old\"*}${new}${s#*\"$old\"}\"",
            "}",
            "",
        ];
        self.write_shell_lines(output, &lines)
    }

    fn write_string_to_upper_function(&self, output: &mut String) -> Result<()> {
        let lines = [
            "rash_string_to_upper() {",
            "    s=\"$1\"",
            "    # POSIX-compliant uppercase conversion",
            "    printf '%s' \"$s\" | tr '[:lower:]' '[:upper:]'",
            "}",
            "",
        ];
        self.write_shell_lines(output, &lines)
    }

    fn write_string_to_lower_function(&self, output: &mut String) -> Result<()> {
        let lines = [
            "rash_string_to_lower() {",
            "    s=\"$1\"",
            "    # POSIX-compliant lowercase conversion",
            "    printf '%s' \"$s\" | tr '[:upper:]' '[:lower:]'",
            "}",
            "",
        ];
        self.write_shell_lines(output, &lines)
    }

    fn write_fs_copy_function(&self, output: &mut String) -> Result<()> {
        let lines = [
            "rash_fs_copy() {",
            "    src=\"$1\"",
            "    dst=\"$2\"",
            "    if [ ! -f \"$src\" ]; then",
            "        echo \"ERROR: Source file not found: $src\" >&2",
            "        return 1",
            "    fi",
            "    cp \"$src\" \"$dst\"",
            "}",
            "",
        ];
        self.write_shell_lines(output, &lines)
    }

    fn write_fs_remove_function(&self, output: &mut String) -> Result<()> {
        let lines = [
            "rash_fs_remove() {",
            "    path=\"$1\"",
            "    if [ ! -e \"$path\" ]; then",
            "        echo \"ERROR: Path not found: $path\" >&2",
            "        return 1",
            "    fi",
            "    rm -f \"$path\"",
            "}",
            "",
        ];
        self.write_shell_lines(output, &lines)
    }

    fn write_fs_is_file_function(&self, output: &mut String) -> Result<()> {
        let lines = [
            "rash_fs_is_file() {",
            "    path=\"$1\"",
            "    test -f \"$path\"",
            "}",
            "",
        ];
        self.write_shell_lines(output, &lines)
    }

    fn write_fs_is_dir_function(&self, output: &mut String) -> Result<()> {
        let lines = [
            "rash_fs_is_dir() {",
            "    path=\"$1\"",
            "    test -d \"$path\"",
            "}",
            "",
        ];
        self.write_shell_lines(output, &lines)
    }

    // Sprint 28: Complete Missing Stdlib Functions - GREEN PHASE

    fn write_string_split_function(&self, output: &mut String) -> Result<()> {
        let lines = [
            "rash_string_split() {",
            "    text=\"$1\"",
            "    delimiter=\"$2\"",
            "    # Use tr to replace delimiter with newline for POSIX compliance",
            "    printf '%s\\n' \"$text\" | tr \"$delimiter\" '\\n'",
            "}",
            "",
        ];
        self.write_shell_lines(output, &lines)
    }

    fn write_array_len_function(&self, output: &mut String) -> Result<()> {
        let lines = [
            "rash_array_len() {",
            "    array=\"$1\"",
            "    # Count non-empty lines",
            "    if [ -z \"$array\" ]; then",
            "        printf '0'",
            "    else",
            "        printf '%s\\n' \"$array\" | wc -l | tr -d ' '",
            "    fi",
            "}",
            "",
        ];
        self.write_shell_lines(output, &lines)
    }

    fn write_array_join_function(&self, output: &mut String) -> Result<()> {
        let lines = [
            "rash_array_join() {",
            "    array=\"$1\"",
            "    separator=\"$2\"",
            "    ",
            "    # Read lines and join with separator",
            "    first=1",
            "    result=\"\"",
            "    while IFS= read -r line; do",
            "        if [ \"$first\" = 1 ]; then",
            "            result=\"$line\"",
            "            first=0",
            "        else",
            "            result=\"${result}${separator}${line}\"",
            "        fi",
            "    done <<EOF",
            "$array",
            "EOF",
            "    printf '%s' \"$result\"",
            "}",
            "",
        ];
        self.write_shell_lines(output, &lines)
    }

    fn needs_runtime(&self) -> bool {
        // For now, always include runtime - later we can analyze the IR to determine if it's needed
        true
    }

    fn emit_ir(&self, output: &mut String, ir: &ShellIR, indent: usize) -> Result<()> {
        match ir {
            ShellIR::Let { name, value, .. } => {
                self.emit_let_statement(output, name, value, indent)
            }
            ShellIR::Exec { cmd, .. } => self.emit_exec_statement(output, cmd, indent),
            ShellIR::If {
                test,
                then_branch,
                else_branch,
            } => self.emit_if_statement(output, test, then_branch, else_branch.as_deref(), indent),
            ShellIR::Exit { code, message } => {
                self.emit_exit_statement(output, (*code).into(), message.as_ref(), indent)
            }
            ShellIR::Sequence(items) => self.emit_sequence(output, items, indent),
            ShellIR::Noop => self.emit_noop(output, indent),
            ShellIR::Function { name, params, body } => {
                self.emit_function(output, name, params, body, indent)
            }
            ShellIR::Echo { value } => self.emit_echo_statement(output, value, indent),
            ShellIR::For {
                var,
                start,
                end,
                body,
            } => self.emit_for_statement(output, var, start, end, body, indent),
            ShellIR::While { condition, body } => {
                self.emit_while_statement(output, condition, body, indent)
            }
            ShellIR::Case { scrutinee, arms } => {
                self.emit_case_statement(output, scrutinee, arms, indent)
            }
            ShellIR::Break => {
                let indent_str = "    ".repeat(indent + 1);
                writeln!(output, "{indent_str}break")?;
                Ok(())
            }
            ShellIR::Continue => {
                let indent_str = "    ".repeat(indent + 1);
                writeln!(output, "{indent_str}continue")?;
                Ok(())
            }
        }
    }

    fn emit_let_statement(
        &self,
        output: &mut String,
        name: &str,
        value: &ShellValue,
        indent: usize,
    ) -> Result<()> {
        let indent_str = "    ".repeat(indent + 1);
        let var_name = escape_variable_name(name);
        let var_value = self.emit_shell_value(value)?;
        // TODO: Add proper variable shadowing with renaming to restore readonly safety
        // For now, use mutable variables to support Rust's let-shadowing semantics
        writeln!(output, "{indent_str}{var_name}={var_value}")?;
        Ok(())
    }

    fn emit_exec_statement(&self, output: &mut String, cmd: &Command, indent: usize) -> Result<()> {
        let indent_str = "    ".repeat(indent + 1);
        let command_str = self.emit_command(cmd)?;
        writeln!(output, "{indent_str}{command_str}")?;
        Ok(())
    }

    fn emit_if_statement(
        &self,
        output: &mut String,
        test: &ShellValue,
        then_branch: &ShellIR,
        else_branch: Option<&ShellIR>,
        indent: usize,
    ) -> Result<()> {
        let indent_str = "    ".repeat(indent + 1);
        let test_expr = self.emit_test_expression(test)?;
        writeln!(output, "{indent_str}if {test_expr}; then")?;

        self.emit_ir(output, then_branch, indent + 1)?;

        if let Some(else_ir) = else_branch {
            writeln!(output, "{indent_str}else")?;
            self.emit_ir(output, else_ir, indent + 1)?;
        }

        writeln!(output, "{indent_str}fi")?;
        Ok(())
    }

    fn emit_exit_statement(
        &self,
        output: &mut String,
        code: i32,
        message: Option<&String>,
        indent: usize,
    ) -> Result<()> {
        let indent_str = "    ".repeat(indent + 1);
        if let Some(msg) = message {
            let escaped_msg = escape_shell_string(msg);
            writeln!(output, "{indent_str}echo {escaped_msg} >&2")?;
        }
        writeln!(output, "{indent_str}exit {code}")?;
        Ok(())
    }

    fn emit_sequence(&self, output: &mut String, items: &[ShellIR], indent: usize) -> Result<()> {
        if items.is_empty() {
            // Empty sequence needs at least ':' for valid POSIX syntax
            self.emit_noop(output, indent)?;
        } else {
            for item in items {
                self.emit_ir(output, item, indent)?;
            }
        }
        Ok(())
    }

    fn emit_noop(&self, output: &mut String, indent: usize) -> Result<()> {
        let indent_str = "    ".repeat(indent + 1);
        // Use ':' (true command) instead of comment for valid POSIX syntax
        writeln!(output, "{indent_str}:")?;
        Ok(())
    }

    fn emit_echo_statement(
        &self,
        output: &mut String,
        value: &ShellValue,
        indent: usize,
    ) -> Result<()> {
        let indent_str = "    ".repeat(indent + 1);
        let value_str = self.emit_shell_value(value)?;
        // Use echo to return value from function
        writeln!(output, "{indent_str}echo {value_str}")?;
        Ok(())
    }

    fn emit_for_statement(
        &self,
        output: &mut String,
        var: &str,
        start: &ShellValue,
        end: &ShellValue,
        body: &ShellIR,
        indent: usize,
    ) -> Result<()> {
        let indent_str = "    ".repeat(indent + 1);
        let var_name = escape_variable_name(var);

        // Emit shell values for start and end
        let start_str = self.emit_shell_value(start)?;
        let end_str = self.emit_shell_value(end)?;

        // Generate POSIX for loop using seq
        // for i in $(seq 0 2); do
        writeln!(
            output,
            "{indent_str}for {var_name} in $(seq {start_str} {end_str}); do"
        )?;

        // Emit body
        self.emit_ir(output, body, indent + 1)?;

        // Close loop
        writeln!(output, "{indent_str}done")?;
        Ok(())
    }

    fn emit_while_statement(
        &self,
        output: &mut String,
        condition: &ShellValue,
        body: &ShellIR,
        indent: usize,
    ) -> Result<()> {
        let indent_str = "    ".repeat(indent + 1);

        // Handle special cases for condition
        let condition_test = match condition {
            ShellValue::Bool(true) => {
                // while true - infinite loop
                "true".to_string()
            }
            ShellValue::Comparison { .. } => {
                // Comparison expression - use emit_shell_value which handles it
                self.emit_shell_value(condition)?
            }
            _ => {
                // General expression - treat as test
                let cond_str = self.emit_shell_value(condition)?;
                format!("[ {cond_str} ]")
            }
        };

        // Emit while loop
        writeln!(output, "{indent_str}while {condition_test}; do")?;

        // Emit body
        self.emit_ir(output, body, indent + 1)?;

        // Close loop
        writeln!(output, "{indent_str}done")?;
        Ok(())
    }

    fn emit_case_statement(
        &self,
        output: &mut String,
        scrutinee: &ShellValue,
        arms: &[crate::ir::shell_ir::CaseArm],
        indent: usize,
    ) -> Result<()> {
        use crate::ir::shell_ir::CasePattern;

        let indent_str = "    ".repeat(indent + 1);
        let scrutinee_str = self.emit_shell_value(scrutinee)?;

        // case "$x" in
        writeln!(output, "{indent_str}case {scrutinee_str} in")?;

        // Emit each case arm
        for arm in arms {
            let pattern_str = match &arm.pattern {
                CasePattern::Literal(lit) => lit.clone(),
                CasePattern::Wildcard => "*".to_string(),
            };

            // TODO: Handle guards with additional if statements inside the case
            if arm.guard.is_some() {
                // For now, guards are not fully supported - would need nested if
                // This is acceptable as it will just be ignored in the emitted code
            }

            // pattern)
            writeln!(output, "{}    {})", indent_str, pattern_str)?;

            // Emit body with additional indentation
            self.emit_ir(output, &arm.body, indent + 1)?;

            // ;;
            writeln!(output, "{}    ;;", indent_str)?;
        }

        // esac
        writeln!(output, "{indent_str}esac")?;
        Ok(())
    }

    fn emit_function(
        &self,
        output: &mut String,
        name: &str,
        params: &[String],
        body: &ShellIR,
        indent: usize,
    ) -> Result<()> {
        // Skip emitting function definitions for known builtins/external commands with empty bodies
        // This prevents user-defined empty stub functions from shadowing shell builtins
        let is_empty_body = matches!(body, ShellIR::Noop)
            || matches!(body, ShellIR::Sequence(items) if items.is_empty());

        if is_empty_body && self.is_known_command(name) {
            // Don't emit the function definition - calls will use the builtin/external command directly
            return Ok(());
        }

        let indent_str = "    ".repeat(indent);
        let body_indent_str = "    ".repeat(indent + 1);

        // Shell function definition
        writeln!(output, "{indent_str}{name}() {{")?;

        // Bind positional parameters to named variables
        for (i, param) in params.iter().enumerate() {
            let pos = i + 1;
            let param_name = escape_variable_name(param);
            // TODO: Restore readonly once proper variable shadowing is implemented
            writeln!(output, "{body_indent_str}{param_name}=\"${pos}\"")?;
        }

        if !params.is_empty() {
            writeln!(output)?;
        }

        // Emit function body
        self.emit_ir(output, body, indent + 1)?;

        writeln!(output, "{indent_str}}}")?;
        writeln!(output)?;

        Ok(())
    }

    /// Check if a name is a known shell builtin or common external command
    fn is_known_command(&self, name: &str) -> bool {
        // POSIX shell builtins
        const BUILTINS: &[&str] = &[
            "echo", "cd", "pwd", "test", "export", "readonly", "shift", "set", "unset", "read",
            "printf", "return", "exit", "trap", "true", "false", ":", ".", "source", "eval",
            "exec", "wait",
        ];

        // Common external commands
        const EXTERNAL_COMMANDS: &[&str] = &[
            "cat", "grep", "sed", "awk", "cut", "sort", "uniq", "wc", "ls", "cp", "mv", "rm",
            "mkdir", "rmdir", "touch", "chmod", "chown", "find", "xargs", "tar", "gzip", "curl",
            "wget", "git", "make", "docker", "ssh", "scp", "rsync",
        ];

        BUILTINS.contains(&name) || EXTERNAL_COMMANDS.contains(&name)
    }

    pub fn emit_shell_value(&self, value: &ShellValue) -> Result<String> {
        match value {
            ShellValue::String(s) => Ok(escape_shell_string(s)),
            ShellValue::Bool(b) => Ok(self.emit_bool_value(*b)),
            ShellValue::Variable(name) => Ok(format!("\"${}\"", escape_variable_name(name))),
            // Sprint 27a: Environment variable expansion
            ShellValue::EnvVar { name, default } => match default {
                None => Ok(format!("\"${{{}}}\"", name)),
                Some(def) => Ok(format!("\"${{{}:-{}}}\"", name, def)),
            },
            ShellValue::Concat(parts) => self.emit_concatenation(parts),
            ShellValue::CommandSubst(cmd) => {
                let cmd_str = self.emit_command(cmd)?;
                Ok(format!("\"$({cmd_str})\""))
            }
            ShellValue::Comparison { op, left, right } => self.emit_comparison(op, left, right),
            ShellValue::Arithmetic { op, left, right } => self.emit_arithmetic(op, left, right),
            ShellValue::LogicalAnd { left, right } => {
                let left_str = self.emit_shell_value(left)?;
                let right_str = self.emit_shell_value(right)?;
                Ok(format!("{left_str} && {right_str}"))
            }
            ShellValue::LogicalOr { left, right } => {
                let left_str = self.emit_shell_value(left)?;
                let right_str = self.emit_shell_value(right)?;
                Ok(format!("{left_str} || {right_str}"))
            }
            ShellValue::LogicalNot { operand } => {
                let operand_str = self.emit_shell_value(operand)?;
                Ok(format!("! {operand_str}"))
            }
            // Sprint 27b: Command-line argument access
            ShellValue::Arg { position } => match position {
                Some(n) => Ok(format!("\"${}\"", n)), // "$1", "$2", etc.
                None => Ok("\"$@\"".to_string()),     // All args
            },
            // P0-POSITIONAL-PARAMETERS: Argument with default value
            ShellValue::ArgWithDefault { position, default } => Ok(format!(
                "\"${{{}:-{}}}\"",
                position,
                escape_shell_string(default)
            )),
            ShellValue::ArgCount => Ok("\"$#\"".to_string()), // Argument count
            // Sprint 27c: Exit code access - GREEN PHASE
            ShellValue::ExitCode => Ok("\"$?\"".to_string()), // Exit code of last command
        }
    }

    fn emit_comparison(
        &self,
        op: &crate::ir::shell_ir::ComparisonOp,
        left: &ShellValue,
        right: &ShellValue,
    ) -> Result<String> {
        use crate::ir::shell_ir::ComparisonOp;

        let left_val = self.emit_shell_value(left)?;
        let right_val = self.emit_shell_value(right)?;

        let op_str = match op {
            ComparisonOp::NumEq => "-eq",
            ComparisonOp::NumNe => "-ne",
            ComparisonOp::Gt => "-gt",
            ComparisonOp::Ge => "-ge",
            ComparisonOp::Lt => "-lt",
            ComparisonOp::Le => "-le",
            ComparisonOp::StrEq => "=",
            ComparisonOp::StrNe => "!=",
        };

        // Generate POSIX test command: [ "$left" -op "$right" ]
        Ok(format!("[ {left_val} {op_str} {right_val} ]"))
    }

    fn emit_arithmetic(
        &self,
        op: &crate::ir::shell_ir::ArithmeticOp,
        left: &ShellValue,
        right: &ShellValue,
    ) -> Result<String> {
        use crate::ir::shell_ir::ArithmeticOp;

        // For arithmetic, emit raw values (no quotes needed inside $((...)))
        let left_str = self.emit_arithmetic_operand(left)?;
        let right_str = self.emit_arithmetic_operand(right)?;

        let op_str = match op {
            ArithmeticOp::Add => "+",
            ArithmeticOp::Sub => "-",
            ArithmeticOp::Mul => "*",
            ArithmeticOp::Div => "/",
            ArithmeticOp::Mod => "%",
        };

        // Generate POSIX arithmetic expansion: $((expr))
        Ok(format!("$(({left_str} {op_str} {right_str}))"))
    }

    fn emit_arithmetic_operand(&self, value: &ShellValue) -> Result<String> {
        match value {
            ShellValue::String(s) => Ok(s.clone()),
            ShellValue::Variable(name) => Ok(escape_variable_name(name)),
            ShellValue::Arithmetic { op, left, right } => {
                // Nested arithmetic - just emit the expression without outer $(())
                let left_str = self.emit_arithmetic_operand(left)?;
                let right_str = self.emit_arithmetic_operand(right)?;
                let op_str = match op {
                    crate::ir::shell_ir::ArithmeticOp::Add => "+",
                    crate::ir::shell_ir::ArithmeticOp::Sub => "-",
                    crate::ir::shell_ir::ArithmeticOp::Mul => "*",
                    crate::ir::shell_ir::ArithmeticOp::Div => "/",
                    crate::ir::shell_ir::ArithmeticOp::Mod => "%",
                };
                Ok(format!("({left_str} {op_str} {right_str})"))
            }
            _ => Err(crate::models::Error::Emission(format!(
                "Unsupported value in arithmetic expression: {:?}",
                value
            ))),
        }
    }

    fn emit_bool_value(&self, value: bool) -> String {
        if value { "true" } else { "false" }.to_string()
    }

    fn emit_concatenation(&self, parts: &[ShellValue]) -> Result<String> {
        let mut result = String::new();
        result.push('"');

        for part in parts {
            self.append_concat_part(&mut result, part)?;
        }

        result.push('"');
        Ok(result)
    }

    fn append_concat_part(&self, result: &mut String, part: &ShellValue) -> Result<()> {
        match part {
            ShellValue::String(s) => result.push_str(s),
            ShellValue::Bool(b) => result.push_str(&self.emit_bool_value(*b)),
            ShellValue::Variable(name) => {
                result.push_str(&format!("${{{}}}", escape_variable_name(name)));
            }
            // Sprint 27a: Environment variable expansion in concatenation
            ShellValue::EnvVar { name, default } => match default {
                None => result.push_str(&format!("${{{}}}", name)),
                Some(def) => result.push_str(&format!("${{{}:-{}}}", name, def)),
            },
            ShellValue::CommandSubst(cmd) => {
                let cmd_str = self.emit_command(cmd)?;
                result.push_str(&format!("$({cmd_str})"));
            }
            ShellValue::Concat(_) => {
                // Nested concatenation - flatten it
                let nested = self.emit_shell_value(part)?;
                self.append_flattened_content(result, &nested);
            }
            ShellValue::Comparison { .. } => {
                // Comparisons don't make sense in concatenation context
                // This should be caught at validation, but handle gracefully
                return Err(crate::models::Error::IrGeneration(
                    "Comparison expression cannot be used in string concatenation".to_string(),
                ));
            }
            ShellValue::Arithmetic { op, left, right } => {
                // Arithmetic in concat context - emit the $((...)) form
                let arith_str = self.emit_arithmetic(op, left, right)?;
                result.push_str(&arith_str);
            }
            ShellValue::LogicalAnd { .. }
            | ShellValue::LogicalOr { .. }
            | ShellValue::LogicalNot { .. } => {
                // Logical operators don't make sense in concatenation context
                return Err(crate::models::Error::IrGeneration(
                    "Logical expression cannot be used in string concatenation".to_string(),
                ));
            }
            // Sprint 27b: Command-line argument access in concatenation
            ShellValue::Arg { position } => match position {
                Some(n) => result.push_str(&format!("${}", n)),
                None => result.push_str("$@"),
            },
            // P0-POSITIONAL-PARAMETERS: Argument with default value in concatenation
            ShellValue::ArgWithDefault { position, default } => {
                result.push_str(&format!("${{{}:-{}}}", position, default));
            }
            ShellValue::ArgCount => {
                result.push_str("$#");
            }
            // Sprint 27c: Exit code in concatenation - GREEN PHASE
            ShellValue::ExitCode => {
                result.push_str("$?");
            }
        }
        Ok(())
    }

    fn append_flattened_content(&self, result: &mut String, nested: &str) {
        // Remove quotes from nested value and add content
        if nested.starts_with('"') && nested.ends_with('"') {
            result.push_str(&nested[1..nested.len() - 1]);
        } else {
            result.push_str(nested);
        }
    }

    fn emit_command(&self, cmd: &Command) -> Result<String> {
        let mut result = escape_command_name(&cmd.program);

        for arg in &cmd.args {
            result.push(' ');
            result.push_str(&self.emit_shell_value(arg)?);
        }

        Ok(result)
    }

    pub fn emit_test_expression(&self, test: &ShellValue) -> Result<String> {
        match test {
            ShellValue::Bool(true) => Ok("true".to_string()),
            ShellValue::Bool(false) => Ok("false".to_string()),
            ShellValue::Variable(name) => {
                // Test if variable is non-empty
                Ok(format!("test -n \"${}\"", escape_variable_name(name)))
            }
            ShellValue::String(s) => {
                if s == "true" || s == "0" {
                    Ok("true".to_string())
                } else {
                    Ok("false".to_string())
                }
            }
            ShellValue::Comparison { .. } => {
                // Comparisons already generate complete test expressions
                self.emit_shell_value(test)
            }
            ShellValue::LogicalAnd { .. }
            | ShellValue::LogicalOr { .. }
            | ShellValue::LogicalNot { .. } => {
                // Logical operators already generate complete test expressions
                self.emit_shell_value(test)
            }
            ShellValue::CommandSubst(cmd) => {
                // Check if this is a predicate function (returns bool via exit code)
                if self.is_predicate_function(&cmd.program) {
                    // Execute directly - exit code is the test result
                    self.emit_command(cmd)
                } else {
                    // For other functions, test if output is non-empty
                    let value = self.emit_shell_value(test)?;
                    Ok(format!("test -n {value}"))
                }
            }
            other => {
                // For complex expressions, evaluate them and test the result
                let value = self.emit_shell_value(other)?;
                Ok(format!("test -n {value}"))
            }
        }
    }

    fn is_predicate_function(&self, name: &str) -> bool {
        // Predicate functions return bool via exit code (0 = true, 1 = false)
        matches!(
            name,
            "rash_string_contains" | "rash_fs_exists" | "test" | "["
        )
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::ir::{Command, ShellIR, ShellValue};
    use crate::models::Config;

    #[test]
    fn test_emit_simple_let() {
        let config = Config::default();
        let emitter = PosixEmitter::new(config);

        let ir = ShellIR::Let {
            name: "test_var".to_string(),
            value: ShellValue::String("hello world".to_string()),
            effects: Default::default(),
        };

        let result = emitter.emit(&ir).unwrap();
        // Updated: Variables are now mutable to support let-shadowing semantics
        assert!(result.contains("test_var='hello world'"));
        assert!(!result.contains("readonly"));
    }

    #[test]
    fn test_emit_command() {
        let config = Config::default();
        let emitter = PosixEmitter::new(config);

        let cmd = Command {
            program: "echo".to_string(),
            args: vec![ShellValue::String("hello".to_string())],
        };

        let ir = ShellIR::Exec {
            cmd,
            effects: Default::default(),
        };

        let result = emitter.emit(&ir).unwrap();
        assert!(result.contains("echo hello"));
    }

    #[test]
    fn test_emit_if_statement() {
        let config = Config::default();
        let emitter = PosixEmitter::new(config);

        let ir = ShellIR::If {
            test: ShellValue::Bool(true),
            then_branch: Box::new(ShellIR::Exec {
                cmd: Command {
                    program: "echo".to_string(),
                    args: vec![ShellValue::String("true branch".to_string())],
                },
                effects: Default::default(),
            }),
            else_branch: None,
        };

        let result = emitter.emit(&ir).unwrap();
        assert!(result.contains("if true; then"));
        assert!(result.contains("echo 'true branch'"));
        assert!(result.contains("fi"));
    }
}
