//! /share command — export the current session as a shareable web URL.
//!
//! Renders the current session transcript as a static HTML page, uploads it
//! to a GitHub Gist via the `gh` CLI, and displays the resulting URL.
//!
//! # Usage
//!
//! - `/share` — export the current session and print the Gist URL
//! - `/share help` — show usage

use std::io::Write;
use std::path::Path;

use super::CommandResult;
use crate::tui::app::{App, AppAction};

/// Share the current session as a web URL.
pub fn share(app: &mut App, arg: Option<&str>) -> CommandResult {
    let raw = arg.map(str::trim).unwrap_or("");

    match raw {
        "" => do_share(app),
        "help" | "--help" | "-h" => CommandResult::message(
            "/share — Export the current session as a shareable web URL.\n\
             \n\
             Usage:\n\
             /share         Export and upload the current session\n\
             \n\
             The session transcript is rendered as static HTML and uploaded\n\
             to a GitHub Gist using the `gh` CLI. The Gist URL is displayed\n\
             so you can paste it into Slack, GitHub, Twitter, etc."
                .to_string(),
        ),
        _ => CommandResult::error(format!(
            "Unknown /share argument `{raw}`. Use `/share` with no arguments or `/share help`."
        )),
    }
}

/// Export the session as HTML, upload to a Gist, and show the URL.
fn do_share(app: &mut App) -> CommandResult {
    // Check if there's any session content to share
    if app.history.is_empty() {
        return CommandResult::error("Nothing to share. The current session is empty.");
    }

    // Sanity-check: the extra info block is optional; the session itself
    // is what we share.
    let history_len = app.history.len();
    let model = &app.model;
    let mode = app.mode.label();

    // Use an AppAction to signal the engine to perform the async work.
    CommandResult::with_message_and_action(
        format!(
            "Exporting {history_len} cell(s) from {model} ({mode}) session...\n\n\
             The session will be rendered as static HTML and uploaded to a GitHub Gist.\n\
             This requires the `gh` CLI to be installed and authenticated."
        ),
        AppAction::ShareSession {
            history_len,
            model: model.clone(),
            mode: mode.to_string(),
        },
    )
}

/// Actually perform the share export.
///
/// This is called from the engine after receiving the `ShareSession` action.
/// It renders the session as HTML and uploads it via `gh gist create`.
pub async fn perform_share(history_json: &str, model: &str, mode: &str) -> Result<String, String> {
    // Build HTML from the session data
    let html = render_session_html(history_json, model, mode);

    // Write to a temp file
    let tmp = match write_temp_html(&html) {
        Ok(file) => file,
        Err(e) => return Err(format!("Failed to write temp file: {e}")),
    };

    // Upload via `gh gist create`
    let url = match upload_gist(tmp.path()).await {
        Ok(url) => url,
        Err(e) => return Err(format!("Failed to upload Gist: {e}")),
    };

    Ok(url)
}

/// Render the session as a standalone HTML page.
fn render_session_html(history_json: &str, model: &str, mode: &str) -> String {
    let timestamp = chrono::Utc::now().format("%Y-%m-%d %H:%M:%S UTC");
    let escaped_model = html_escape(model);
    let escaped_mode = html_escape(mode);
    let escaped_body = html_escape(history_json);

    format!(
        r#"<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>codewhale Session Export</title>
<style>
  body {{
    font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
    max-width: 800px; margin: 2rem auto; padding: 0 1rem;
    background: #0d1117; color: #c9d1d9;
  }}
  h1 {{ color: #58a6ff; border-bottom: 1px solid #30363d; padding-bottom: 0.5rem; }}
  .meta {{ color: #8b949e; font-size: 0.9rem; margin-bottom: 2rem; }}
  .message {{ margin: 1rem 0; padding: 0.75rem; border-radius: 6px; }}
  .user {{ background: #1f2937; border-left: 3px solid #58a6ff; }}
  .assistant {{ background: #161b22; border-left: 3px solid #3fb950; }}
  .tool {{ background: #0d1117; border: 1px solid #30363d; font-family: monospace; font-size: 0.85rem; }}
  pre {{ white-space: pre-wrap; word-wrap: break-word; margin: 0; }}
  .footer {{ margin-top: 2rem; padding-top: 1rem; border-top: 1px solid #30363d; color: #8b949e; font-size: 0.8rem; }}
</style>
</head>
<body>
<h1>codewhale Session</h1>
<div class="meta">
  <strong>Model:</strong> {escaped_model} · <strong>Mode:</strong> {escaped_mode}<br>
  <strong>Exported:</strong> {timestamp}
</div>
<pre>{escaped_body}</pre>
<div class="footer">
  Generated by codewhale · https://github.com/Hmbown/CodeWhale
</div>
</body>
</html>"#,
    )
}

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

/// Write HTML to a secure temp file and keep it alive for upload.
fn write_temp_html(html: &str) -> Result<tempfile::NamedTempFile, String> {
    let mut tmp = tempfile::Builder::new()
        .prefix("codewhale-share-")
        .suffix(".html")
        .tempfile()
        .map_err(|e| format!("{e}"))?;
    tmp.write_all(html.as_bytes()).map_err(|e| format!("{e}"))?;
    Ok(tmp)
}

/// Upload a file as a GitHub Gist using the `gh` CLI.
async fn upload_gist(path: &Path) -> Result<String, String> {
    let output = tokio::process::Command::new("gh")
        .args([
            "gist",
            "create",
            "--public",
            &path.to_string_lossy(),
            "--filename",
            "session-export.html",
            "--desc",
            "codewhale Session Export",
        ])
        .output()
        .await
        .map_err(|e| format!("Failed to run `gh gist create`: {e}"))?;

    if !output.status.success() {
        let stderr = String::from_utf8_lossy(&output.stderr);
        return Err(format!("`gh gist create` failed: {stderr}"));
    }

    let stdout = String::from_utf8_lossy(&output.stdout).trim().to_string();
    if stdout.is_empty() {
        return Err("`gh gist create` returned no output".to_string());
    }

    Ok(stdout)
}

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

    #[test]
    fn test_render_session_html_basic_structure() {
        let html = render_session_html("[{}]", "deepseek-v4-pro", "agent");
        assert!(html.contains("<!DOCTYPE html>"));
        assert!(html.contains("deepseek-v4-pro"));
        assert!(html.contains("agent"));
        assert!(html.contains("[{}]"));
        assert!(html.contains("codewhale"));
    }

    #[test]
    fn test_html_escape_handles_special_chars() {
        assert_eq!(html_escape("<script>"), "&lt;script&gt;");
        assert_eq!(html_escape("a&b"), "a&amp;b");
        assert_eq!(html_escape("\"quote\""), "&quot;quote&quot;");
    }

    #[test]
    fn test_write_temp_html_creates_file() {
        let file = write_temp_html("<html></html>").unwrap();
        assert!(file.path().exists());
        let content = std::fs::read_to_string(file.path()).unwrap();
        assert_eq!(content, "<html></html>");
    }

    #[test]
    fn test_render_session_html_metadata() {
        let html = render_session_html("test data", "deepseek-v4-flash", "plan");
        assert!(html.contains("deepseek-v4-flash"));
        assert!(html.contains("plan"));
        assert!(html.contains("test data"));
        assert!(html.contains("Exported:"));
        assert!(html.contains("https://github.com/Hmbown/CodeWhale"));
    }
}
