use anyhow::Result;
use clap::{Args, Subcommand};

use crate::commands::Context;
use crate::output;
use crate::permit;

#[derive(Subcommand)]
pub enum StreamAction {
    /// Open a payment stream
    Open(StreamOpenArgs),
    /// Close a payment stream
    Close(StreamCloseArgs),
    /// Withdraw accrued funds from a stream
    Withdraw(StreamIdArgs),
    /// List active streams
    List(StreamListArgs),
}

#[derive(Args)]
pub struct StreamIdArgs {
    /// Stream ID
    pub stream_id: String,
}

#[derive(Args)]
pub struct StreamOpenArgs {
    /// Recipient wallet address
    pub payee: String,
    /// USDC per second (e.g., 0.001)
    pub rate: String,
    /// Maximum total USDC to stream
    pub max: String,
}

#[derive(Args)]
pub struct StreamCloseArgs {
    /// Stream ID
    pub stream_id: String,
}

#[derive(Args)]
pub struct StreamListArgs {
    #[arg(long, default_value = "20")]
    pub limit: u32,
}

pub async fn run(action: StreamAction, ctx: Context) -> Result<()> {
    super::require_init()?;
    let mut client = ctx.client()?;

    match action {
        StreamAction::Open(args) => {
            super::validate_positive_amount(&args.rate, "rate")?;
            super::validate_positive_amount(&args.max, "max")?;
            super::validate_address(&args.payee, "payee")?;
            let permit_sig = permit::auto_permit(&mut client, &args.max, "stream").await?;
            let stream = client
                .stream_open(&args.payee, &args.rate, &args.max, Some(&permit_sig))
                .await?;
            if ctx.json {
                output::print_json(&stream);
            } else {
                output::success(&format!("Stream opened: {}", stream.id));
                print_stream(&stream);
            }
        }

        StreamAction::Close(args) => {
            let stream = client.stream_close(&args.stream_id).await?;
            if ctx.json {
                output::print_json(&stream);
            } else {
                output::success(&format!("Stream {} closed", args.stream_id));
                print_stream(&stream);
            }
        }

        StreamAction::Withdraw(args) => {
            let stream = client.withdraw_stream(&args.stream_id).await?;
            if ctx.json {
                output::print_json(&stream);
            } else {
                output::success(&format!("Stream {} withdrawn", args.stream_id));
                print_stream(&stream);
            }
        }

        StreamAction::List(args) => {
            let paged = client.streams_list(args.limit).await?;
            if ctx.json {
                output::print_json(&paged);
            } else if paged.data.is_empty() {
                output::info("No active streams.");
            } else {
                output::print_table(
                    vec!["ID", "Payee", "Rate/s", "Max", "Withdrawn", "Status"],
                    paged
                        .data
                        .iter()
                        .map(|s| {
                            vec![
                                s.id.clone(),
                                s.payee.clone().unwrap_or_else(|| "—".to_string()),
                                s.rate_per_second
                                    .as_ref()
                                    .map(|v| v.to_string())
                                    .unwrap_or_else(|| "—".to_string()),
                                s.max_total
                                    .as_ref()
                                    .map(|v| v.to_string())
                                    .unwrap_or_else(|| "—".to_string()),
                                s.withdrawn
                                    .as_ref()
                                    .map(|v| v.to_string())
                                    .unwrap_or_else(|| "0".to_string()),
                                s.status.clone(),
                            ]
                        })
                        .collect(),
                );
            }
        }
    }
    Ok(())
}

fn print_stream(stream: &crate::client::Stream) {
    output::print_kv(&[
        ("ID", stream.id.as_str()),
        ("Status", stream.status.as_str()),
        ("Payee", stream.payee.as_deref().unwrap_or("—")),
        (
            "Rate/s",
            &stream
                .rate_per_second
                .as_ref()
                .map(|v| format!("{v} USDC"))
                .unwrap_or_else(|| "—".to_string()),
        ),
        (
            "Max total",
            &stream
                .max_total
                .as_ref()
                .map(|v| format!("{v} USDC"))
                .unwrap_or_else(|| "—".to_string()),
        ),
        ("Tx Hash", stream.tx_hash.as_deref().unwrap_or("—")),
    ]);
}
