//! HIR WidgetNode → Kotlin/Glance source emitter
//!
//! Walks the declarative widget tree and emits valid Kotlin Glance composables.

use perry_hir::ir::*;
use std::fmt::Write;

/// Emit the main GlanceAppWidget Kotlin class
pub fn emit_widget(widget: &WidgetDecl, name: &str, package: &str) -> String {
    let mut out = String::new();
    let entry_param = &widget.entry_param_name;

    writeln!(out, "// Auto-generated by Perry compiler — do not edit").unwrap();
    writeln!(out, "package {}", package).unwrap();
    writeln!(out).unwrap();
    writeln!(out, "import android.content.Context").unwrap();
    writeln!(out, "import androidx.glance.*").unwrap();
    writeln!(out, "import androidx.glance.appwidget.*").unwrap();
    writeln!(out, "import androidx.glance.layout.*").unwrap();
    writeln!(out, "import androidx.glance.text.*").unwrap();
    writeln!(out, "import androidx.compose.ui.unit.dp").unwrap();
    writeln!(out, "import androidx.compose.ui.unit.sp").unwrap();
    writeln!(out, "import androidx.compose.ui.graphics.Color").unwrap();
    writeln!(out).unwrap();

    // Data class for entry
    write!(out, "data class {}Entry(", name).unwrap();
    let field_strs: Vec<String> = widget
        .entry_fields
        .iter()
        .map(|(fname, ftype)| format!("val {}: {}", fname, kotlin_type_for_field(ftype)))
        .collect();
    write!(out, "{}", field_strs.join(", ")).unwrap();
    writeln!(out, ")").unwrap();
    writeln!(out).unwrap();

    // GlanceAppWidget class
    writeln!(out, "class {}Widget : GlanceAppWidget() {{", name).unwrap();
    writeln!(
        out,
        "    override suspend fun provideGlance(context: Context, id: GlanceId) {{"
    )
    .unwrap();
    writeln!(out, "        provideContent {{").unwrap();

    if widget.provider_func_name.is_some() {
        writeln!(
            out,
            "            val {} = {}Bridge.fetchEntry(context)",
            entry_param, name
        )
        .unwrap();
    } else {
        // Create a default entry
        write!(out, "            val {} = {}Entry(", entry_param, name).unwrap();
        let defaults: Vec<String> = widget
            .entry_fields
            .iter()
            .map(|(_, ftype)| kotlin_default_value(ftype))
            .collect();
        write!(out, "{}", defaults.join(", ")).unwrap();
        writeln!(out, ")").unwrap();
    }

    // Emit the composable tree
    if widget.render_body.is_empty() {
        writeln!(out, "            Text(text = \"Empty widget\")").unwrap();
    } else {
        for node in &widget.render_body {
            out.push_str(&emit_node(node, entry_param, 3));
        }
    }

    writeln!(out, "        }}").unwrap();
    writeln!(out, "    }}").unwrap();
    writeln!(out, "}}").unwrap();
    out
}

/// Emit GlanceAppWidgetReceiver
pub fn emit_receiver(name: &str, package: &str) -> String {
    let mut out = String::new();
    writeln!(out, "// Auto-generated by Perry compiler — do not edit").unwrap();
    writeln!(out, "package {}", package).unwrap();
    writeln!(out).unwrap();
    writeln!(out, "import androidx.glance.appwidget.GlanceAppWidget").unwrap();
    writeln!(
        out,
        "import androidx.glance.appwidget.GlanceAppWidgetReceiver"
    )
    .unwrap();
    writeln!(out).unwrap();
    writeln!(out, "class {}Receiver : GlanceAppWidgetReceiver() {{", name).unwrap();
    writeln!(
        out,
        "    override val glanceAppWidget: GlanceAppWidget = {}Widget()",
        name
    )
    .unwrap();
    writeln!(out, "}}").unwrap();
    out
}

/// Emit configuration Activity (if config params exist)
pub fn emit_config_activity(widget: &WidgetDecl, name: &str, package: &str) -> String {
    let mut out = String::new();
    writeln!(out, "// Auto-generated by Perry compiler — do not edit").unwrap();
    writeln!(out, "package {}", package).unwrap();
    writeln!(out).unwrap();
    writeln!(out, "import android.app.Activity").unwrap();
    writeln!(out, "import android.appwidget.AppWidgetManager").unwrap();
    writeln!(out, "import android.content.Intent").unwrap();
    writeln!(out, "import android.os.Bundle").unwrap();
    writeln!(out, "import android.widget.LinearLayout").unwrap();
    writeln!(out, "import android.widget.Spinner").unwrap();
    writeln!(out, "import android.widget.ArrayAdapter").unwrap();
    writeln!(out, "import android.widget.Button").unwrap();
    writeln!(out, "import android.widget.Switch").unwrap();
    writeln!(out, "import android.content.SharedPreferences").unwrap();
    writeln!(out).unwrap();

    writeln!(out, "class {}ConfigActivity : Activity() {{", name).unwrap();
    writeln!(
        out,
        "    private var appWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID"
    )
    .unwrap();
    writeln!(out).unwrap();
    writeln!(
        out,
        "    override fun onCreate(savedInstanceState: Bundle?) {{"
    )
    .unwrap();
    writeln!(out, "        super.onCreate(savedInstanceState)").unwrap();
    writeln!(out, "        setResult(RESULT_CANCELED)").unwrap();
    writeln!(out).unwrap();
    writeln!(out, "        appWidgetId = intent?.extras?.getInt(").unwrap();
    writeln!(
        out,
        "            AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID"
    )
    .unwrap();
    writeln!(out, "        ) ?: AppWidgetManager.INVALID_APPWIDGET_ID").unwrap();
    writeln!(out).unwrap();
    writeln!(
        out,
        "        if (appWidgetId == AppWidgetManager.INVALID_APPWIDGET_ID) {{"
    )
    .unwrap();
    writeln!(out, "            finish()").unwrap();
    writeln!(out, "            return").unwrap();
    writeln!(out, "        }}").unwrap();
    writeln!(out).unwrap();
    writeln!(
        out,
        "        val layout = LinearLayout(this).apply {{ orientation = LinearLayout.VERTICAL }}"
    )
    .unwrap();

    for param in &widget.config_params {
        match &param.param_type {
            WidgetConfigParamType::Enum { values, default } => {
                writeln!(out, "        val {}Spinner = Spinner(this)", param.name).unwrap();
                writeln!(
                    out,
                    "        val {}Values = arrayOf({})",
                    param.name,
                    values
                        .iter()
                        .map(|v| format!("\"{}\"", v))
                        .collect::<Vec<_>>()
                        .join(", ")
                )
                .unwrap();
                writeln!(out, "        {}Spinner.adapter = ArrayAdapter(this, android.R.layout.simple_spinner_item, {}Values).apply {{", param.name, param.name).unwrap();
                writeln!(out, "            setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)").unwrap();
                writeln!(out, "        }}").unwrap();
                let default_idx = values.iter().position(|v| v == default).unwrap_or(0);
                writeln!(
                    out,
                    "        {}Spinner.setSelection({})",
                    param.name, default_idx
                )
                .unwrap();
                writeln!(out, "        layout.addView({}Spinner)", param.name).unwrap();
            }
            WidgetConfigParamType::Bool { default } => {
                writeln!(
                    out,
                    "        val {}Switch = Switch(this).apply {{ text = \"{}\"; isChecked = {} }}",
                    param.name, param.title, default
                )
                .unwrap();
                writeln!(out, "        layout.addView({}Switch)", param.name).unwrap();
            }
            WidgetConfigParamType::String { .. } => {
                writeln!(
                    out,
                    "        // String param: {} (use EditText)",
                    param.name
                )
                .unwrap();
            }
        }
    }

    writeln!(out).unwrap();
    writeln!(
        out,
        "        val saveButton = Button(this).apply {{ text = \"Save\" }}"
    )
    .unwrap();
    writeln!(out, "        saveButton.setOnClickListener {{").unwrap();
    writeln!(out, "            val prefs = getSharedPreferences(\"widget_$appWidgetId\", MODE_PRIVATE).edit()").unwrap();
    for param in &widget.config_params {
        match &param.param_type {
            WidgetConfigParamType::Enum { .. } => {
                writeln!(
                    out,
                    "            prefs.putString(\"{}\", {}Spinner.selectedItem.toString())",
                    param.name, param.name
                )
                .unwrap();
            }
            WidgetConfigParamType::Bool { .. } => {
                writeln!(
                    out,
                    "            prefs.putBoolean(\"{}\", {}Switch.isChecked)",
                    param.name, param.name
                )
                .unwrap();
            }
            _ => {}
        }
    }
    writeln!(out, "            prefs.apply()").unwrap();
    writeln!(out, "            val result = Intent().putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId)").unwrap();
    writeln!(out, "            setResult(RESULT_OK, result)").unwrap();
    writeln!(out, "            finish()").unwrap();
    writeln!(out, "        }}").unwrap();
    writeln!(out, "        layout.addView(saveButton)").unwrap();
    writeln!(out, "        setContentView(layout)").unwrap();
    writeln!(out, "    }}").unwrap();
    writeln!(out, "}}").unwrap();
    out
}

/// Emit widget_info XML
pub fn emit_widget_info_xml(widget: &WidgetDecl, name: &str) -> String {
    let (min_width, min_height) = widget_dimensions(&widget.supported_families);

    let mut out = String::new();
    writeln!(out, "<?xml version=\"1.0\" encoding=\"utf-8\"?>").unwrap();
    write!(
        out,
        "<appwidget-provider xmlns:android=\"http://schemas.android.com/apk/res/android\""
    )
    .unwrap();
    writeln!(out).unwrap();
    writeln!(out, "    android:minWidth=\"{}dp\"", min_width).unwrap();
    writeln!(out, "    android:minHeight=\"{}dp\"", min_height).unwrap();
    writeln!(out, "    android:updatePeriodMillis=\"1800000\"").unwrap();
    writeln!(
        out,
        "    android:initialLayout=\"@layout/glance_default_loading_layout\""
    )
    .unwrap();
    writeln!(out, "    android:resizeMode=\"horizontal|vertical\"").unwrap();
    writeln!(out, "    android:widgetCategory=\"home_screen\"").unwrap();

    if !widget.config_params.is_empty() {
        writeln!(
            out,
            "    android:configure=\"PACKAGE.{}ConfigActivity\"",
            name
        )
        .unwrap();
    }

    if !widget.description.is_empty() {
        writeln!(
            out,
            "    android:description=\"@string/{}_description\"",
            name.to_lowercase()
        )
        .unwrap();
    }

    writeln!(out, "    />").unwrap();
    out
}

/// Emit AndroidManifest receiver snippet
pub fn emit_manifest_snippet(widget: &WidgetDecl, name: &str, package: &str) -> String {
    let mut out = String::new();
    writeln!(
        out,
        "<!-- Add to AndroidManifest.xml inside <application> -->"
    )
    .unwrap();
    writeln!(out, "<receiver").unwrap();
    writeln!(out, "    android:name=\"{}.{}Receiver\"", package, name).unwrap();
    writeln!(out, "    android:exported=\"true\">").unwrap();
    writeln!(out, "    <intent-filter>").unwrap();
    writeln!(
        out,
        "        <action android:name=\"android.appwidget.action.APPWIDGET_UPDATE\" />"
    )
    .unwrap();
    writeln!(out, "    </intent-filter>").unwrap();
    writeln!(out, "    <meta-data").unwrap();
    writeln!(out, "        android:name=\"android.appwidget.provider\"").unwrap();
    writeln!(
        out,
        "        android:resource=\"@xml/widget_info_{}\" />",
        name.to_lowercase()
    )
    .unwrap();
    writeln!(out, "</receiver>").unwrap();

    if !widget.config_params.is_empty() {
        writeln!(out).unwrap();
        writeln!(out, "<activity").unwrap();
        writeln!(
            out,
            "    android:name=\"{}.{}ConfigActivity\"",
            package, name
        )
        .unwrap();
        writeln!(out, "    android:exported=\"true\">").unwrap();
        writeln!(out, "    <intent-filter>").unwrap();
        writeln!(
            out,
            "        <action android:name=\"android.appwidget.action.APPWIDGET_CONFIGURE\" />"
        )
        .unwrap();
        writeln!(out, "    </intent-filter>").unwrap();
        writeln!(out, "</activity>").unwrap();
    }

    out
}

/// Map supported families to Android widget dimensions
fn widget_dimensions(families: &[String]) -> (u32, u32) {
    let mut min_width = 110u32;
    let mut min_height = 110u32;

    for family in families {
        match family.as_str() {
            "systemMedium" => {
                min_width = 250;
            }
            "systemLarge" => {
                min_width = 250;
                min_height = 250;
            }
            _ => {}
        }
    }

    (min_width, min_height)
}

/// Emit a single WidgetNode as Glance Kotlin composable
fn emit_node(node: &WidgetNode, entry_param: &str, indent: usize) -> String {
    let mut out = String::new();
    let pad = "    ".repeat(indent);

    match node {
        WidgetNode::Text { content, modifiers } => {
            let text_arg = emit_text_content(content, entry_param);
            write!(out, "{}Text(text = {})", pad, text_arg).unwrap();
            // TODO: emit Glance text style modifiers
            let _ = modifiers;
            writeln!(out).unwrap();
        }
        WidgetNode::Stack {
            kind,
            spacing,
            children,
            modifiers,
        } => {
            let composable = match kind {
                WidgetStackKind::VStack => "Column",
                WidgetStackKind::HStack => "Row",
                WidgetStackKind::ZStack => "Box",
            };
            let modifier_str = emit_glance_modifiers(modifiers, spacing);
            if modifier_str.is_empty() {
                writeln!(out, "{}{}  {{", pad, composable).unwrap();
            } else {
                writeln!(out, "{}{}(modifier = {}) {{", pad, composable, modifier_str).unwrap();
            }
            for child in children {
                out.push_str(&emit_node(child, entry_param, indent + 1));
            }
            writeln!(out, "{}}}", pad).unwrap();
        }
        WidgetNode::Image {
            system_name,
            modifiers,
        } => {
            // Map SF Symbol names to Android drawable resources
            let _ = modifiers;
            writeln!(
                out,
                "{}// Image: {} (provide R.drawable resource)",
                pad, system_name
            )
            .unwrap();
            writeln!(out, "{}Image(provider = ImageProvider(R.drawable.ic_placeholder), contentDescription = \"{}\")", pad, system_name).unwrap();
        }
        WidgetNode::Spacer => {
            writeln!(out, "{}Spacer()", pad).unwrap();
        }
        WidgetNode::Divider => {
            writeln!(out, "{}Spacer(modifier = GlanceModifier.height(1.dp).fillMaxWidth().background(Color.Gray))", pad).unwrap();
        }
        WidgetNode::Conditional {
            field,
            op,
            value,
            then_node,
            else_node,
        } => {
            let cond = emit_kotlin_condition(field, op, value, entry_param);
            writeln!(out, "{}if ({}) {{", pad, cond).unwrap();
            out.push_str(&emit_node(then_node, entry_param, indent + 1));
            if let Some(else_n) = else_node {
                writeln!(out, "{}}} else {{", pad).unwrap();
                out.push_str(&emit_node(else_n, entry_param, indent + 1));
            }
            writeln!(out, "{}}}", pad).unwrap();
        }
        WidgetNode::ForEach {
            collection_field,
            item_param,
            body,
        } => {
            writeln!(
                out,
                "{}{}.{}.forEach {{ {} ->",
                pad, entry_param, collection_field, item_param
            )
            .unwrap();
            out.push_str(&emit_node(body, item_param, indent + 1));
            writeln!(out, "{}}}", pad).unwrap();
        }
        WidgetNode::Label {
            text,
            system_image,
            modifiers,
        } => {
            let _ = modifiers;
            let text_arg = emit_text_content(text, entry_param);
            writeln!(out, "{}Row {{", pad).unwrap();
            writeln!(
                out,
                "{}    // Icon: {} (provide R.drawable resource)",
                pad, system_image
            )
            .unwrap();
            writeln!(out, "{}    Text(text = {})", pad, text_arg).unwrap();
            writeln!(out, "{}}}", pad).unwrap();
        }
        WidgetNode::FamilySwitch { cases, default } => {
            writeln!(
                out,
                "{}// Family switch — check LocalSize.current for different layouts",
                pad
            )
            .unwrap();
            writeln!(out, "{}val currentSize = LocalSize.current", pad).unwrap();
            for (i, (family_value, node)) in cases.iter().enumerate() {
                let (width_check, _) = family_to_size_check(family_value);
                if i == 0 {
                    writeln!(out, "{}if ({}) {{", pad, width_check).unwrap();
                } else {
                    writeln!(out, "{}}} else if ({}) {{", pad, width_check).unwrap();
                }
                out.push_str(&emit_node(node, entry_param, indent + 1));
            }
            if let Some(default_node) = default {
                writeln!(out, "{}}} else {{", pad).unwrap();
                out.push_str(&emit_node(default_node, entry_param, indent + 1));
            }
            writeln!(out, "{}}}", pad).unwrap();
        }
        WidgetNode::Gauge {
            value_expr,
            label,
            style,
            modifiers,
        } => {
            let _ = (style, modifiers);
            writeln!(
                out,
                "{}// Gauge not natively supported in Glance — using text fallback",
                pad
            )
            .unwrap();
            writeln!(
                out,
                "{}Text(text = \"{}: ${{{}.{}}}\")",
                pad, label, entry_param, value_expr
            )
            .unwrap();
        }
    }

    out
}

/// Emit text content as Kotlin string expression
fn emit_text_content(content: &WidgetTextContent, entry_param: &str) -> String {
    match content {
        WidgetTextContent::Literal(s) => format!("\"{}\"", escape_kotlin_string(s)),
        WidgetTextContent::Field(field) => format!("\"${{{}.{}}}\"", entry_param, field),
        WidgetTextContent::Template(parts) => {
            let mut s = String::from("\"");
            for part in parts {
                match part {
                    WidgetTemplatePart::Literal(lit) => s.push_str(&escape_kotlin_string(lit)),
                    WidgetTemplatePart::Field(field) => {
                        write!(s, "${{{}.{}}}", entry_param, field).unwrap()
                    }
                    WidgetTemplatePart::Formatted(expr) => {
                        write!(s, "${{{}}}", emit_format_kotlin(expr, entry_param)).unwrap()
                    }
                }
            }
            s.push('"');
            s
        }
        WidgetTextContent::Formatted(expr) => {
            // Wrap the produced Kotlin expression in a string template
            // so Text(...) consumes a String — same as the Field arm.
            format!("\"${{{}}}\"", emit_format_kotlin(expr, entry_param))
        }
    }
}

/// Issue #1179 follow-up: emit a Kotlin expression for one of the
/// whitelisted formatter calls. Result is a String-typed expression.
fn emit_format_kotlin(expr: &WidgetFormatExpr, entry_param: &str) -> String {
    let value = emit_format_arg_kotlin(&expr.arg, entry_param);
    match expr.call {
        WidgetFormatCall::StringCast | WidgetFormatCall::ToString => {
            format!("{}.toString()", value)
        }
        WidgetFormatCall::NumberCast => format!("{}.toDouble()", value),
        WidgetFormatCall::Round => format!("kotlin.math.round({}).toInt()", value),
        WidgetFormatCall::Floor => format!("kotlin.math.floor({}).toInt()", value),
        WidgetFormatCall::Ceil => format!("kotlin.math.ceil({}).toInt()", value),
        WidgetFormatCall::ToFixed { digits } => {
            format!("\"%.{}f\".format({})", digits, value)
        }
    }
}

fn emit_format_arg_kotlin(arg: &WidgetFormatArg, entry_param: &str) -> String {
    match arg {
        WidgetFormatArg::Field(field) => format!("{}.{}", entry_param, field),
        WidgetFormatArg::Number(n) => {
            if n.is_finite() && *n == n.floor() {
                format!("{:.0}", n)
            } else {
                format!("{}", n)
            }
        }
        WidgetFormatArg::String(s) => format!("\"{}\"", escape_kotlin_string(s)),
    }
}

/// Emit Glance modifier chain from WidgetModifiers
fn emit_glance_modifiers(modifiers: &[WidgetModifier], spacing: &Option<f64>) -> String {
    let mut parts = Vec::new();

    if let Some(sp) = spacing {
        // Glance doesn't have a direct spacing prop — approximate with padding
        let _ = sp;
    }

    for modifier in modifiers {
        match modifier {
            WidgetModifier::Padding(p) => parts.push(format!("padding({}. dp)", *p as u32)),
            WidgetModifier::Background(color) => {
                let c = kotlin_color_expr(color);
                parts.push(format!("background(day = {}, night = {})", c, c));
            }
            WidgetModifier::CornerRadius(r) => {
                parts.push(format!("cornerRadius({}. dp)", *r as u32))
            }
            WidgetModifier::FrameMaxWidth => parts.push("fillMaxWidth()".to_string()),
            WidgetModifier::WidgetURL(url) => {
                parts.push(format!("clickable(actionStartActivity(android.content.Intent(android.content.Intent.ACTION_VIEW, android.net.Uri.parse(\"{}\"))))", escape_kotlin_string(url)));
            }
            _ => {} // Other modifiers not directly supported in Glance
        }
    }

    if parts.is_empty() {
        String::new()
    } else {
        format!("GlanceModifier.{}", parts.join("."))
    }
}

/// Emit a condition as Kotlin expression
fn emit_kotlin_condition(
    field: &str,
    op: &WidgetConditionOp,
    value: &WidgetTextContent,
    entry_param: &str,
) -> String {
    let lhs = format!("{}.{}", entry_param, field);
    match op {
        WidgetConditionOp::Truthy => lhs,
        WidgetConditionOp::GreaterThan => format!("{} > {}", lhs, emit_kotlin_value(value)),
        WidgetConditionOp::LessThan => format!("{} < {}", lhs, emit_kotlin_value(value)),
        WidgetConditionOp::Equals => format!("{} == {}", lhs, emit_kotlin_value(value)),
        WidgetConditionOp::NotEquals => format!("{} != {}", lhs, emit_kotlin_value(value)),
    }
}

fn emit_kotlin_value(value: &WidgetTextContent) -> String {
    match value {
        WidgetTextContent::Literal(s) => {
            if let Ok(n) = s.parse::<f64>() {
                if n == n.floor() {
                    format!("{:.0}", n)
                } else {
                    format!("{}", n)
                }
            } else {
                format!("\"{}\"", escape_kotlin_string(s))
            }
        }
        WidgetTextContent::Field(f) => f.clone(),
        WidgetTextContent::Template(_) => "\"\"".to_string(),
        // Conditions don't compare against formatted expressions today.
        WidgetTextContent::Formatted(_) => "\"\"".to_string(),
    }
}

/// Map family to size check condition for Glance
fn family_to_size_check(family: &str) -> (String, String) {
    match family {
        "systemSmall" => (
            "currentSize.width < 200.dp".to_string(),
            "small".to_string(),
        ),
        "systemMedium" => (
            "currentSize.width >= 200.dp && currentSize.height < 200.dp".to_string(),
            "medium".to_string(),
        ),
        "systemLarge" => (
            "currentSize.width >= 200.dp && currentSize.height >= 200.dp".to_string(),
            "large".to_string(),
        ),
        _ => (format!("true /* {} */", family), family.to_string()),
    }
}

/// Map WidgetFieldType to Kotlin type
fn kotlin_type_for_field(field_type: &WidgetFieldType) -> String {
    match field_type {
        WidgetFieldType::String => "String".to_string(),
        WidgetFieldType::Number => "Double".to_string(),
        WidgetFieldType::Boolean => "Boolean".to_string(),
        WidgetFieldType::Array(inner) => format!("List<{}>", kotlin_type_for_field(inner)),
        WidgetFieldType::Optional(inner) => format!("{}?", kotlin_type_for_field(inner)),
        WidgetFieldType::Object(fields) => {
            // Use a Map<String, Any> for nested objects (or could generate data class)
            let _ = fields;
            "Map<String, Any>".to_string()
        }
    }
}

/// Default value for a Kotlin field type
fn kotlin_default_value(field_type: &WidgetFieldType) -> String {
    match field_type {
        WidgetFieldType::String => "\"\"".to_string(),
        WidgetFieldType::Number => "0.0".to_string(),
        WidgetFieldType::Boolean => "false".to_string(),
        WidgetFieldType::Array(_) => "emptyList()".to_string(),
        WidgetFieldType::Optional(_) => "null".to_string(),
        WidgetFieldType::Object(_) => "emptyMap()".to_string(),
    }
}

/// Convert color name to Kotlin Color expression
fn kotlin_color_expr(color: &str) -> String {
    match color {
        "red" => "Color.Red".to_string(),
        "blue" => "Color.Blue".to_string(),
        "green" => "Color.Green".to_string(),
        "white" => "Color.White".to_string(),
        "black" => "Color.Black".to_string(),
        "gray" | "grey" => "Color.Gray".to_string(),
        "yellow" => "Color.Yellow".to_string(),
        _ => format!("Color.{}", color),
    }
}

/// Escape a string for Kotlin string literals
fn escape_kotlin_string(s: &str) -> String {
    s.replace('\\', "\\\\")
        .replace('"', "\\\"")
        .replace('\n', "\\n")
        .replace('\r', "\\r")
        .replace('\t', "\\t")
        .replace('$', "\\$")
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_simple_text_widget() {
        let widget = WidgetDecl {
            kind: "com.test.Hello".to_string(),
            display_name: "Hello".to_string(),
            description: "A hello widget".to_string(),
            supported_families: vec!["systemSmall".to_string()],
            entry_fields: vec![("greeting".to_string(), WidgetFieldType::String)],
            render_body: vec![WidgetNode::Text {
                content: WidgetTextContent::Field("greeting".to_string()),
                modifiers: vec![],
            }],
            entry_param_name: "entry".to_string(),
            config_params: vec![],
            provider_func_name: None,
            placeholder: None,
            family_param_name: None,
            app_group: None,
            reload_after_seconds: None,
        };

        let kt = emit_widget(&widget, "Hello", "com.test");
        assert!(kt.contains("class HelloWidget : GlanceAppWidget()"));
        assert!(kt.contains("data class HelloEntry"));
        assert!(kt.contains("Text(text = \"${entry.greeting}\")"));
    }

    #[test]
    fn test_widget_info_xml() {
        let widget = WidgetDecl {
            kind: "com.test.Hello".to_string(),
            display_name: "Hello".to_string(),
            description: "A test widget".to_string(),
            supported_families: vec!["systemMedium".to_string()],
            entry_fields: vec![],
            render_body: vec![],
            entry_param_name: "entry".to_string(),
            config_params: vec![],
            provider_func_name: None,
            placeholder: None,
            family_param_name: None,
            app_group: None,
            reload_after_seconds: None,
        };

        let xml = emit_widget_info_xml(&widget, "Hello");
        assert!(xml.contains("android:minWidth=\"250dp\""));
        assert!(xml.contains("android:minHeight=\"110dp\""));
    }
}
