//! `pic logs <script-id>` — print recent execution log rows.
//!
//! In TSV mode emits a header + truncated-summary rows (`pic logs` was
//! previously headerless — inconsistent with `apps ls` / `scripts ls`).
//! In JSON mode emits the raw `ExecutionLog` array (no truncation),
//! letting `jq` consumers see request/response bodies in full.

use anyhow::Result;
use picloud_shared::{ExecutionLog, ExecutionStatus};

use crate::client::Client;
use crate::config;
use crate::output::{OutputMode, Table};

pub async fn run(
    script_id: &str,
    limit: u32,
    source: Option<&str>,
    mode: OutputMode,
) -> Result<()> {
    let creds = config::resolve()?;
    let client = Client::from_creds(&creds)?;
    let entries = client.logs_list(script_id, limit, source).await?;
    match mode {
        OutputMode::Tsv => render_tsv(&entries),
        OutputMode::Json => render_json(&entries),
    }
    Ok(())
}

fn render_tsv(entries: &[ExecutionLog]) {
    // `source` is shown so background runs (queue/cron/invoke/…) are
    // distinguishable from HTTP at a glance — the whole point of G1.
    let mut table = Table::new(["created_at", "source", "status", "summary"]);
    for e in entries {
        let summary = summarize(&e.response_body, &e.script_logs);
        table.row([
            e.created_at.to_rfc3339(),
            e.source.as_str().to_string(),
            status_label(&e.status).to_string(),
            truncate(&summary, 120),
        ]);
    }
    table.print(OutputMode::Tsv);
}

fn render_json(entries: &[ExecutionLog]) {
    // Pretty for human jq-piping; consumers that want compact can pipe
    // through `jq -c`.
    let s = serde_json::to_string_pretty(entries).unwrap_or_else(|_| "[]".to_string());
    println!("{s}");
}

fn status_label(s: &ExecutionStatus) -> &'static str {
    match s {
        ExecutionStatus::Success => "success",
        ExecutionStatus::Error => "error",
        ExecutionStatus::Timeout => "timeout",
        ExecutionStatus::BudgetExceeded => "budget_exceeded",
    }
}

fn summarize(response_body: &Option<serde_json::Value>, script_logs: &serde_json::Value) -> String {
    // Prefer the last script-side log line (often the most useful for
    // grepping). Fall back to the response body.
    if let Some(arr) = script_logs.as_array() {
        if let Some(last) = arr.last() {
            if let Some(msg) = last.get("message").and_then(|m| m.as_str()) {
                return msg.to_string();
            }
        }
    }
    response_body
        .as_ref()
        .map(ToString::to_string)
        .unwrap_or_else(|| "-".to_string())
}

fn truncate(s: &str, n: usize) -> String {
    let normalized = s.replace('\n', " ");
    if normalized.chars().count() <= n {
        normalized
    } else {
        let head: String = normalized.chars().take(n).collect();
        format!("{head}…")
    }
}
