//! Roundtrip summary and diff generation
//!
//! Generates human-readable summaries of roundtrip operations showing
//! what changed between input and output.

use serde_json::Value;
use std::collections::HashMap;

/// Summary of a roundtrip operation
#[derive(Debug, Clone)]
pub struct RoundtripSummary {
    /// Total number of fields processed
    pub total_fields: usize,

    /// Number of fields that changed
    pub changed_fields: usize,

    /// Number of fields unchanged
    pub unchanged_fields: usize,

    /// List of field changes (field_name, old_value, new_value)
    pub changes: Vec<FieldChange>,

    /// Validation status
    pub validation_passed: Option<bool>,

    /// Output file path
    pub output_path: String,
}

/// Represents a change in a field value
#[derive(Debug, Clone)]
pub struct FieldChange {
    pub field_name: String,
    pub old_value: String,
    pub new_value: String,
    pub changed: bool,
}

impl RoundtripSummary {
    /// Create a summary by comparing initial and final values
    pub fn from_values(
        initial_values: &HashMap<String, Value>,
        final_results: &HashMap<String, Value>,
        validation_passed: Option<bool>,
        output_path: String,
    ) -> Self {
        let mut changes = Vec::new();
        let mut changed_count = 0;
        let mut unchanged_count = 0;

        // Collect all field names from both maps
        let mut all_fields: Vec<String> = initial_values
            .keys()
            .chain(final_results.keys())
            .cloned()
            .collect();
        all_fields.sort();
        all_fields.dedup();

        for field_name in all_fields {
            let old_val = initial_values.get(&field_name);
            let new_val = final_results.get(&field_name);

            let old_str = value_to_display_string(old_val);
            let new_str = value_to_display_string(new_val);

            let changed = old_str != new_str;
            if changed {
                changed_count += 1;
            } else {
                unchanged_count += 1;
            }

            changes.push(FieldChange {
                field_name,
                old_value: old_str,
                new_value: new_str,
                changed,
            });
        }

        Self {
            total_fields: changes.len(),
            changed_fields: changed_count,
            unchanged_fields: unchanged_count,
            changes,
            validation_passed,
            output_path,
        }
    }

    /// Render summary as terminal text with colors
    pub fn render_terminal(&self, verbose: bool) -> String {
        let mut output = String::new();

        output.push('\n');
        output.push_str("╔════════════════════════════════════════════════════════════╗\n");
        output.push_str("  ✅ Configuration Saved Successfully!\n");
        output.push_str("════════════════════════════════════════════════════════════\n");
        output.push_str(&format!("  📄 File: {}\n", self.output_path));

        if let Some(passed) = self.validation_passed {
            let status = if passed { "✓ PASSED" } else { "✗ FAILED" };
            output.push_str(&format!("  ✓ Validation: {}\n", status));
        }

        output.push_str(&format!(
            "  📊 Fields: {}/{} changed, {} unchanged\n",
            self.changed_fields, self.total_fields, self.unchanged_fields
        ));

        output.push_str("════════════════════════════════════════════════════════════\n");

        // Show changes
        if self.changed_fields > 0 {
            output.push_str("  📋 What Changed:\n");

            let mut shown = 0;
            for change in &self.changes {
                if !change.changed {
                    continue;
                }

                if !verbose && shown >= 10 {
                    let remaining = self.changed_fields - shown;
                    output.push_str(&format!(
                        "    ... and {} more changes (use --verbose to see all)\n",
                        remaining
                    ));
                    break;
                }

                output.push_str(&format!(
                    "    ├─ {}: {} → {}\n",
                    change.field_name,
                    truncate(&change.old_value, 20),
                    truncate(&change.new_value, 20)
                ));
                shown += 1;
            }
        } else {
            output.push_str("  📋 No changes made\n");
        }

        output.push_str("════════════════════════════════════════════════════════════\n");
        output.push_str("  💡 Next Steps:\n");
        output.push_str("    • Review: cat config.ncl\n");
        output.push_str("    • Apply CI tools: ./setup-ci.sh\n");
        output.push_str("    • Re-configure: ./ci-configure.sh\n");
        output.push_str("╚════════════════════════════════════════════════════════════╝\n");
        output.push('\n');

        output
    }

    /// Render summary as HTML page for web backend
    pub fn render_html(&self) -> String {
        let validation_badge = match self.validation_passed {
            Some(true) => r#"<span style="color: #4ec9b0;">✓ PASSED</span>"#,
            Some(false) => r#"<span style="color: #f48771;">✗ FAILED</span>"#,
            None => r#"<span style="color: #808080;">N/A</span>"#,
        };

        let changes_html = if self.changed_fields > 0 {
            let mut html = String::from("<div style='margin: 20px 0;'>");
            html.push_str("<h3 style='margin: 0 0 10px 0;'>📋 What Changed:</h3>");
            html.push_str("<div style='background: #1e1e1e; padding: 15px; border-radius: 4px; font-family: monospace;'>");

            for change in &self.changes {
                if !change.changed {
                    continue;
                }

                html.push_str(&format!(
                    r#"<div style="margin: 8px 0;">
                        <div style="color: #dcdcaa;">{}</div>
                        <div style="margin-left: 20px; color: #f48771;">- {}</div>
                        <div style="margin-left: 20px; color: #4ec9b0;">+ {}</div>
                    </div>"#,
                    html_escape(&change.field_name),
                    html_escape(&change.old_value),
                    html_escape(&change.new_value)
                ));
            }

            html.push_str("</div></div>");
            html
        } else {
            String::from("<p style='color: #808080;'>No changes made</p>")
        };

        format!(
            r#"<!DOCTYPE html>
<html>
<head>
    <title>Configuration Saved</title>
    <meta charset="UTF-8">
    <style>
        body {{
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
            background: #1e1e1e;
            color: #d4d4d4;
            margin: 0;
            padding: 40px 20px;
        }}
        .container {{
            max-width: 800px;
            margin: 0 auto;
            background: #252526;
            padding: 40px;
            border-radius: 8px;
            box-shadow: 0 4px 6px rgba(0, 0, 0, 0.3);
        }}
        h1 {{
            color: #4ec9b0;
            margin: 0 0 10px 0;
        }}
        .header {{
            border-bottom: 2px solid #007acc;
            padding-bottom: 20px;
            margin-bottom: 30px;
        }}
        .stat-row {{
            display: flex;
            justify-content: space-between;
            margin: 10px 0;
            padding: 10px;
            background: #1e1e1e;
            border-radius: 4px;
        }}
        .actions {{
            margin-top: 30px;
            padding-top: 20px;
            border-top: 1px solid #3e3e42;
        }}
        .btn {{
            display: inline-block;
            padding: 10px 20px;
            margin: 5px;
            background: #007acc;
            color: white;
            text-decoration: none;
            border-radius: 4px;
            border: none;
            cursor: pointer;
        }}
        .btn:hover {{
            background: #005a9e;
        }}
        .next-steps {{
            background: #1e1e1e;
            padding: 15px;
            border-radius: 4px;
            border-left: 3px solid #4ec9b0;
            margin-top: 20px;
        }}
        .next-steps ul {{
            margin: 10px 0;
            padding-left: 20px;
        }}
        .auto-close {{
            color: #808080;
            font-size: 12px;
            margin-top: 30px;
            text-align: center;
        }}
    </style>
    <script>
        let countdown = 30;
        function updateCountdown() {{
            document.getElementById('countdown').textContent = countdown;
            countdown--;
            if (countdown < 0) {{
                window.close();
            }}
        }}
        setInterval(updateCountdown, 1000);
    </script>
</head>
<body>
    <div class="container">
        <div class="header">
            <h1>✅ Configuration Saved Successfully!</h1>
        </div>

        <div class="stat-row">
            <span>📄 <strong>File:</strong></span>
            <span style="font-family: monospace;">{}</span>
        </div>

        <div class="stat-row">
            <span>✓ <strong>Validation:</strong></span>
            <span>{}</span>
        </div>

        <div class="stat-row">
            <span>📊 <strong>Fields Configured:</strong></span>
            <span>{} total ({} changed, {} unchanged)</span>
        </div>

        {}

        <div class="next-steps">
            <h3 style="margin: 0 0 10px 0;">💡 Next Steps:</h3>
            <ul>
                <li>Review configuration: <code>cat {}</code></li>
                <li>Apply CI tools: <code>./setup-ci.sh</code></li>
                <li>Re-configure anytime: <code>./ci-configure.sh</code></li>
            </ul>
        </div>

        <div class="actions">
            <button class="btn" onclick="window.location.href='/api/form/download'">📥 Download Config</button>
            <button class="btn" onclick="window.location.reload()">🔄 Configure Again</button>
            <button class="btn" onclick="window.close()">❌ Close</button>
        </div>

        <div class="auto-close">
            ⏱️  This window will auto-close in <span id="countdown">30</span> seconds...
        </div>
    </div>
</body>
</html>"#,
            html_escape(&self.output_path),
            validation_badge,
            self.total_fields,
            self.changed_fields,
            self.unchanged_fields,
            changes_html,
            html_escape(&self.output_path)
        )
    }
}

/// Convert a JSON Value to a display string
fn value_to_display_string(value: Option<&Value>) -> String {
    match value {
        Some(Value::String(s)) => s.clone(),
        Some(Value::Number(n)) => n.to_string(),
        Some(Value::Bool(b)) => b.to_string(),
        Some(Value::Array(arr)) => {
            let items: Vec<String> = arr
                .iter()
                .map(|v| match v {
                    Value::String(s) => s.clone(),
                    other => other.to_string(),
                })
                .collect();
            format!("[{}]", items.join(", "))
        }
        Some(Value::Null) => String::from("(empty)"),
        Some(Value::Object(_)) => String::from("{...}"),
        None => String::from("(not set)"),
    }
}

/// Truncate string to max length with ellipsis
fn truncate(s: &str, max_len: usize) -> String {
    if s.len() <= max_len {
        s.to_string()
    } else {
        format!("{}...", &s[..max_len.saturating_sub(3)])
    }
}

/// HTML escape for safe rendering
fn html_escape(s: &str) -> String {
    s.replace('&', "&amp;")
        .replace('<', "&lt;")
        .replace('>', "&gt;")
        .replace('"', "&quot;")
        .replace('\'', "&#x27;")
}

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

    #[test]
    fn test_summary_with_changes() {
        let initial = HashMap::from([
            ("field1".to_string(), json!("value1")),
            ("field2".to_string(), json!(42)),
        ]);

        let final_results = HashMap::from([
            ("field1".to_string(), json!("value_changed")),
            ("field2".to_string(), json!(42)),
        ]);

        let summary = RoundtripSummary::from_values(
            &initial,
            &final_results,
            Some(true),
            "config.ncl".to_string(),
        );

        assert_eq!(summary.total_fields, 2);
        assert_eq!(summary.changed_fields, 1);
        assert_eq!(summary.unchanged_fields, 1);
    }

    #[test]
    fn test_truncate() {
        assert_eq!(truncate("short", 10), "short");
        assert_eq!(truncate("this is a very long string", 10), "this is...");
    }
}
