/// Convert command — React/TSX → DreamStack converter.

use std::fs;
use std::path::Path;

pub fn cmd_convert(name: &str, shadcn: bool, output: Option<&Path>) {
    let tsx_source = if shadcn {
        // Fetch from shadcn/ui GitHub
        let url = format!(
            "https://raw.githubusercontent.com/shadcn-ui/taxonomy/main/components/ui/{}.tsx",
            name
        );
        println!("   📥 Fetching {}.tsx from shadcn/ui...", name);
        match fetch_url_blocking(&url) {
            Ok(source) => source,
            Err(e) => {
                println!("   ❌ Failed to fetch: {e}");
                println!("   Try providing a local .tsx file instead.");
                return;
            }
        }
    } else {
        // Read local file
        match fs::read_to_string(name) {
            Ok(source) => source,
            Err(e) => {
                println!("   ❌ Cannot read '{name}': {e}");
                return;
            }
        }
    };

    let ds_output = convert_tsx_to_ds(&tsx_source, name);

    if let Some(out_path) = output {
        fs::write(out_path, &ds_output).expect("Failed to write output file");
        println!("   ✅ Converted to {}", out_path.display());
    } else {
        println!("{}", ds_output);
    }
}

/// Best-effort TSX → DreamStack converter.
/// Pattern-matches common React/shadcn idioms rather than full TypeScript parsing.
fn convert_tsx_to_ds(tsx: &str, file_hint: &str) -> String {
    let mut out = String::new();

    // Extract component name
    let comp_name = extract_component_name(tsx)
        .unwrap_or_else(|| {
            // Derive from filename
            let base = Path::new(file_hint)
                .file_stem()
                .and_then(|s| s.to_str())
                .unwrap_or("Component");
            let mut chars = base.chars();
            match chars.next() {
                Some(c) => format!("{}{}", c.to_uppercase().collect::<String>(), chars.collect::<String>()),
                None => "Component".to_string(),
            }
        });

    // Extract props
    let props = extract_props(tsx);

    // Header comment
    out.push_str(&format!("-- Converted from {}\n", file_hint));
    out.push_str("-- Auto-generated by dreamstack convert\n\n");

    // Extract useState hooks → let declarations
    let state_vars = extract_use_state(tsx);
    for (name, default) in &state_vars {
        out.push_str(&format!("let {} = {}\n", name, default));
    }
    if !state_vars.is_empty() {
        out.push('\n');
    }

    // Extract cva variants
    let variants = extract_cva_variants(tsx);
    if !variants.is_empty() {
        out.push_str("-- Variants:\n");
        for (variant_name, values) in &variants {
            out.push_str(&format!("--   {}: {}\n", variant_name, values.join(", ")));
        }
        out.push('\n');
    }

    // Extract JSX body
    let jsx_body = extract_jsx_body(tsx);

    // Build component declaration
    let props_str = if props.is_empty() {
        String::new()
    } else {
        format!("({})", props.join(", "))
    };

    out.push_str(&format!("export component {}{} =\n", comp_name, props_str));

    if jsx_body.is_empty() {
        out.push_str("  text \"TODO: convert JSX body\"\n");
    } else {
        out.push_str(&jsx_body);
    }

    out
}

/// Extract component name from React.forwardRef or function/const declaration
fn extract_component_name(tsx: &str) -> Option<String> {
    // Pattern: `const Button = React.forwardRef`
    for line in tsx.lines() {
        let trimmed = line.trim();
        if trimmed.contains("forwardRef") || trimmed.contains("React.forwardRef") {
            if let Some(pos) = trimmed.find("const ") {
                let rest = &trimmed[pos + 6..];
                if let Some(eq_pos) = rest.find(" =") {
                    return Some(rest[..eq_pos].trim().to_string());
                }
            }
        }
        // Pattern: `export function Button(`
        if trimmed.starts_with("export function ") || trimmed.starts_with("function ") {
            let rest = trimmed.strip_prefix("export ").unwrap_or(trimmed);
            let rest = rest.strip_prefix("function ").unwrap_or(rest);
            if let Some(paren) = rest.find('(') {
                let name = rest[..paren].trim();
                if name.chars().next().map_or(false, |c| c.is_uppercase()) {
                    return Some(name.to_string());
                }
            }
        }
    }
    None
}

/// Extract props from destructured function parameters
fn extract_props(tsx: &str) -> Vec<String> {
    let mut props = Vec::new();
    // Look for destructured props: `({ className, variant, size, ...props })`
    if let Some(start) = tsx.find("({ ") {
        if let Some(end) = tsx[start..].find(" })") {
            let inner = &tsx[start + 3..start + end];
            for part in inner.split(',') {
                let part = part.trim();
                if part.starts_with("...") { continue; }  // skip rest props
                if part.is_empty() { continue; }
                // Handle defaults: `variant = "default"` → just `variant`
                let name = part.split('=').next().unwrap_or(part).trim();
                let name = name.split(':').next().unwrap_or(name).trim();
                if !name.is_empty()
                    && name != "className"
                    && name != "ref"
                    && name != "children"
                    && !props.contains(&name.to_string())
                {
                    props.push(name.to_string());
                }
            }
        }
    }
    props
}

/// Extract useState hooks: `const [open, setOpen] = useState(false)` → ("open", "false")
fn extract_use_state(tsx: &str) -> Vec<(String, String)> {
    let mut states = Vec::new();
    for line in tsx.lines() {
        let trimmed = line.trim();
        if trimmed.contains("useState") {
            // const [name, setName] = useState(default)
            if let Some(bracket_start) = trimmed.find('[') {
                if let Some(comma) = trimmed[bracket_start..].find(',') {
                    let name = trimmed[bracket_start + 1..bracket_start + comma].trim();
                    // Extract default value from useState(...)
                    if let Some(paren_start) = trimmed.find("useState(") {
                        let rest = &trimmed[paren_start + 9..];
                        if let Some(paren_end) = rest.find(')') {
                            let default = rest[..paren_end].trim();
                            let ds_default = convert_value(default);
                            states.push((name.to_string(), ds_default));
                        }
                    }
                }
            }
        }
    }
    states
}

/// Extract cva variant definitions
fn extract_cva_variants(tsx: &str) -> Vec<(String, Vec<String>)> {
    let mut variants = Vec::new();
    let mut in_variants = false;
    let mut current_variant = String::new();
    let mut current_values = Vec::new();

    for line in tsx.lines() {
        let trimmed = line.trim();
        if trimmed == "variants: {" {
            in_variants = true;
            continue;
        }
        if in_variants {
            if trimmed == "}," || trimmed == "}" {
                if !current_variant.is_empty() {
                    variants.push((current_variant.clone(), current_values.clone()));
                    current_variant.clear();
                    current_values.clear();
                }
                if trimmed == "}," {
                    // Could be end of a variant group or end of variants
                    if trimmed == "}," { continue; }
                }
                in_variants = false;
                continue;
            }
            // Variant group: `variant: {`
            if trimmed.ends_with(": {") || trimmed.ends_with(":{") {
                if !current_variant.is_empty() {
                    variants.push((current_variant.clone(), current_values.clone()));
                    current_values.clear();
                }
                current_variant = trimmed.split(':').next().unwrap_or("").trim().to_string();
                continue;
            }
            // Variant value: `default: "bg-primary text-primary-foreground",`
            if trimmed.contains(":") && !current_variant.is_empty() {
                let name = trimmed.split(':').next().unwrap_or("").trim().trim_matches('"');
                if !name.is_empty() {
                    current_values.push(name.to_string());
                }
            }
        }
    }
    if !current_variant.is_empty() {
        variants.push((current_variant, current_values));
    }
    variants
}

/// Convert a JSX body to DreamStack view syntax (best-effort)
fn extract_jsx_body(tsx: &str) -> String {
    let mut out = String::new();
    let mut in_return = false;
    let mut depth = 0;

    for line in tsx.lines() {
        let trimmed = line.trim();

        // Find the return statement
        if trimmed.starts_with("return (") || trimmed == "return (" {
            in_return = true;
            depth = 1;
            continue;
        }

        if !in_return { continue; }

        // Track parens
        for c in trimmed.chars() {
            match c {
                '(' => depth += 1,
                ')' => {
                    depth -= 1;
                    if depth <= 0 {
                        in_return = false;
                    }
                }
                _ => {}
            }
        }

        if !in_return && depth <= 0 { break; }

        // Convert JSX elements
        let converted = convert_jsx_line(trimmed);
        if !converted.is_empty() {
            out.push_str("  ");
            out.push_str(&converted);
            out.push('\n');
        }
    }

    out
}

/// Convert a single JSX line to DreamStack syntax
fn convert_jsx_line(jsx: &str) -> String {
    let trimmed = jsx.trim();

    // Skip closing tags
    if trimmed.starts_with("</") { return String::new(); }
    // Skip fragments
    if trimmed == "<>" || trimmed == "</>" { return String::new(); }
    // Skip className-only attributes
    if trimmed.starts_with("className=") { return String::new(); }

    // Self-closing tag: `<Component prop="val" />`
    if trimmed.starts_with('<') && trimmed.ends_with("/>") {
        let inner = &trimmed[1..trimmed.len() - 2].trim();
        let parts: Vec<&str> = inner.splitn(2, ' ').collect();
        let tag = parts[0];
        let ds_tag = convert_html_tag(tag);
        if parts.len() > 1 {
            let attrs = convert_jsx_attrs(parts[1]);
            return format!("{} {{ {} }}", ds_tag, attrs);
        }
        return ds_tag;
    }

    // Opening tag: `<button ... >`
    if trimmed.starts_with('<') && !trimmed.starts_with("</") {
        let close = trimmed.find('>').unwrap_or(trimmed.len());
        let inner = &trimmed[1..close].trim();
        let parts: Vec<&str> = inner.splitn(2, ' ').collect();
        let tag = parts[0];
        let ds_tag = convert_html_tag(tag);

        // Check for text content after >
        if close < trimmed.len() - 1 {
            let content = trimmed[close + 1..].trim();
            let content = content.trim_end_matches(&format!("</{}>", tag));
            if !content.is_empty() {
                return format!("{} \"{}\"", ds_tag, content);
            }
        }

        if parts.len() > 1 {
            let attrs = convert_jsx_attrs(parts[1]);
            return format!("{} {{ {} }}", ds_tag, attrs);
        }
        return ds_tag;
    }

    // JSX expression: `{children}`, `{title}`
    if trimmed.starts_with('{') && trimmed.ends_with('}') {
        let expr = &trimmed[1..trimmed.len() - 1].trim();
        // Conditional: `{condition && <X/>}`
        if expr.contains(" && ") {
            let parts: Vec<&str> = expr.splitn(2, " && ").collect();
            return format!("-- when {} -> ...", parts[0]);
        }
        return format!("text {}", expr);
    }

    // Plain text content
    if !trimmed.is_empty() && !trimmed.starts_with("//") && !trimmed.starts_with("/*") {
        return format!("-- {}", trimmed);
    }

    String::new()
}

/// Convert HTML tag to DreamStack element
fn convert_html_tag(tag: &str) -> String {
    match tag {
        "button" => "button".to_string(),
        "input" => "input".to_string(),
        "div" => "column [".to_string(),
        "span" => "text".to_string(),
        "p" => "text".to_string(),
        "h1" | "h2" | "h3" | "h4" | "h5" | "h6" => "text".to_string(),
        "img" => "image".to_string(),
        "a" => "link".to_string(),
        "label" => "label".to_string(),
        "form" => "column [".to_string(),
        "ul" | "ol" => "column [".to_string(),
        "li" => "text".to_string(),
        // Capitalized = component use
        _ if tag.chars().next().map_or(false, |c| c.is_uppercase()) => {
            format!("{}", tag)
        }
        _ => format!("-- unknown: <{}>", tag),
    }
}

/// Convert JSX attributes to DreamStack props
fn convert_jsx_attrs(attrs: &str) -> String {
    let mut props = Vec::new();
    // Simple: key="value" or key={expr}
    let mut remaining = attrs.trim().trim_end_matches('>').trim_end_matches('/').trim();
    while !remaining.is_empty() {
        // Skip className
        if remaining.starts_with("className=") {
            // Skip to next attr
            if let Some(quote_end) = skip_attr_value(remaining) {
                remaining = remaining[quote_end..].trim();
                continue;
            }
            break;
        }
        // Skip ref
        if remaining.starts_with("ref=") {
            if let Some(quote_end) = skip_attr_value(remaining) {
                remaining = remaining[quote_end..].trim();
                continue;
            }
            break;
        }
        // Skip {...props}
        if remaining.starts_with("{...") {
            if let Some(end) = remaining.find('}') {
                remaining = remaining[end + 1..].trim();
                continue;
            }
            break;
        }

        // Parse key=value
        if let Some(eq_pos) = remaining.find('=') {
            let key = remaining[..eq_pos].trim();
            let rest = remaining[eq_pos + 1..].trim();

            let ds_key = convert_event_name(key);

            if rest.starts_with('"') {
                // String value
                if let Some(end) = rest[1..].find('"') {
                    let val = &rest[1..1 + end];
                    props.push(format!("{}: \"{}\"", ds_key, val));
                    remaining = rest[end + 2..].trim();
                } else {
                    break;
                }
            } else if rest.starts_with('{') {
                // Expression value
                if let Some(end) = find_matching_brace(rest) {
                    let expr = &rest[1..end].trim();
                    props.push(format!("{}: {}", ds_key, expr));
                    remaining = rest[end + 1..].trim();
                } else {
                    break;
                }
            } else {
                break;
            }
        } else {
            break;
        }
    }
    props.join(", ")
}

fn convert_event_name(name: &str) -> String {
    match name {
        "onClick" => "click".to_string(),
        "onChange" => "change".to_string(),
        "onSubmit" => "submit".to_string(),
        "onKeyDown" => "keydown".to_string(),
        "onFocus" => "focus".to_string(),
        "onBlur" => "blur".to_string(),
        "disabled" => "disabled".to_string(),
        "placeholder" => "placeholder".to_string(),
        "type" => "type".to_string(),
        "value" => "value".to_string(),
        "href" => "href".to_string(),
        "src" => "src".to_string(),
        "alt" => "alt".to_string(),
        _ => name.to_string(),
    }
}

fn convert_value(val: &str) -> String {
    match val {
        "true" => "true".to_string(),
        "false" => "false".to_string(),
        "null" | "undefined" => "0".to_string(),
        s if s.starts_with('"') => s.to_string(),
        s if s.starts_with('\'') => format!("\"{}\"", &s[1..s.len()-1]),
        s => s.to_string(),
    }
}

fn skip_attr_value(s: &str) -> Option<usize> {
    let eq = s.find('=')?;
    let rest = &s[eq + 1..];
    if rest.starts_with('"') {
        let end = rest[1..].find('"')?;
        Some(eq + 1 + end + 2)
    } else if rest.starts_with('{') {
        let end = find_matching_brace(rest)?;
        Some(eq + 1 + end + 1)
    } else {
        Some(eq + 1)
    }
}

fn find_matching_brace(s: &str) -> Option<usize> {
    let mut depth = 0;
    for (i, c) in s.chars().enumerate() {
        match c {
            '{' => depth += 1,
            '}' => {
                depth -= 1;
                if depth == 0 { return Some(i); }
            }
            _ => {}
        }
    }
    None
}

/// Simple blocking HTTP fetch (no async runtime needed)
fn fetch_url_blocking(url: &str) -> Result<String, String> {
    // Use std::process to call curl
    let output = std::process::Command::new("curl")
        .args(["-sL", "--fail", url])
        .output()
        .map_err(|e| format!("Failed to run curl: {e}"))?;

    if !output.status.success() {
        return Err(format!("HTTP request failed (status {})", output.status));
    }

    String::from_utf8(output.stdout)
        .map_err(|e| format!("Invalid UTF-8 in response: {e}"))
}
