//! `pic dead-letters` subcommands: `ls`, `show`, `replay`, `resolve`.

use anyhow::Result;

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

/// `pic dead-letters count --app <slug>` — print the unresolved DL
/// count for an app. Useful for headless probes (alerting / dashboards).
pub async fn count(app: &str, mode: OutputMode) -> Result<()> {
    let creds = config::resolve()?;
    let client = Client::from_creds(&creds)?;
    let resp = client.dead_letters_count(app).await?;
    let mut table = Table::new(["unresolved"]);
    table.row([resp.unresolved.to_string()]);
    table.print(mode);
    Ok(())
}

/// `pic dead-letters ls` — list rows for an app (`--app`) or a group's shared
/// queues (`--group`, §11.6 D3; list-only, no show/replay/resolve — those stay
/// app-only, matching the server's operator surface).
pub async fn ls(
    app: Option<&str>,
    group: Option<&str>,
    unresolved: bool,
    limit: u32,
    mode: OutputMode,
) -> Result<()> {
    let creds = config::resolve()?;
    let client = Client::from_creds(&creds)?;
    match crate::cmds::require_one_owner(app, group)? {
        crate::cmds::OwnerRef::App(a) => {
            let resp = client.dead_letters_list(a, unresolved, limit).await?;
            let mut table = Table::new([
                "id",
                "source",
                "op",
                "attempts",
                "resolved",
                "last_error",
                "created_at",
            ]);
            for d in resp.dead_letters {
                let resolved = d.resolution.unwrap_or_else(|| "-".into());
                table.row([
                    d.id,
                    d.source,
                    d.op,
                    d.attempt_count.to_string(),
                    resolved,
                    truncate(&d.last_error, 60),
                    d.created_at.to_rfc3339(),
                ]);
            }
            table.print(mode);
        }
        crate::cmds::OwnerRef::Group(g) => {
            let rows = client.group_dead_letters_list(g, unresolved, limit).await?;
            // A group DL carries the `collection` it belonged to and no trigger.
            let mut table = Table::new([
                "id",
                "collection",
                "source",
                "op",
                "attempts",
                "resolved",
                "last_error",
                "created_at",
            ]);
            for d in rows {
                let resolved = d.resolved_at.unwrap_or_else(|| "-".into());
                table.row([
                    d.id,
                    d.collection,
                    d.source,
                    d.op,
                    d.attempt_count.to_string(),
                    resolved,
                    truncate(&d.last_error, 60),
                    d.created_at,
                ]);
            }
            table.print(mode);
        }
    }
    Ok(())
}

pub async fn show(app: &str, dl_id: &str, mode: OutputMode) -> Result<()> {
    let creds = config::resolve()?;
    let client = Client::from_creds(&creds)?;
    let d = client.dead_letters_get(app, dl_id).await?;
    let mut block = KvBlock::new();
    block
        .field("id", d.id)
        .field("source", d.source)
        .field("op", d.op)
        .field("trigger_id", d.trigger_id.unwrap_or_else(|| "-".into()))
        .field(
            "script_id",
            d.script_id
                .map(|s| s.to_string())
                .unwrap_or_else(|| "-".into()),
        )
        .field("attempt_count", d.attempt_count.to_string())
        .field("first_attempt_at", d.first_attempt_at.to_rfc3339())
        .field("last_attempt_at", d.last_attempt_at.to_rfc3339())
        .field("last_error", d.last_error)
        .field("created_at", d.created_at.to_rfc3339())
        .field(
            "resolved_at",
            d.resolved_at
                .map(|t| t.to_rfc3339())
                .unwrap_or_else(|| "-".into()),
        )
        .field("resolution", d.resolution.unwrap_or_else(|| "-".into()))
        .field("payload", d.payload.to_string());
    block.print(mode);
    Ok(())
}

pub async fn replay(app: &str, dl_id: &str) -> Result<()> {
    let creds = config::resolve()?;
    let client = Client::from_creds(&creds)?;
    client.dead_letters_replay(app, dl_id).await?;
    println!("Replayed dead-letter {dl_id}");
    Ok(())
}

pub async fn resolve(app: &str, dl_id: &str, reason: &str) -> Result<()> {
    let creds = config::resolve()?;
    let client = Client::from_creds(&creds)?;
    client.dead_letters_resolve(app, dl_id, reason).await?;
    println!("Resolved dead-letter {dl_id}: {reason}");
    Ok(())
}

fn truncate(s: &str, max: usize) -> String {
    if s.chars().count() <= max {
        return s.to_string();
    }
    let mut out: String = s.chars().take(max.saturating_sub(1)).collect();
    out.push('…');
    out
}
