use anyhow::Result;
use clap::{Args, Subcommand};
use std::time::{SystemTime, UNIX_EPOCH};

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

#[derive(Subcommand)]
pub enum DepositAction {
    /// Create a new deposit (refundable collateral)
    Create(DepositCreateArgs),
    /// Return a deposit (release collateral back to depositor)
    Return(DepositIdArgs),
    /// Forfeit a deposit (provider claims the collateral)
    Forfeit(DepositIdArgs),
}

#[derive(Args)]
pub struct DepositIdArgs {
    /// Deposit ID
    pub deposit_id: String,
}

#[derive(Args)]
pub struct DepositCreateArgs {
    /// Provider wallet address
    pub provider: String,
    /// Amount in USDC
    pub amount: String,
    /// Expiry in seconds from now (default: 86400 = 24h)
    #[arg(long, default_value = "86400")]
    pub expiry: u64,
}

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

    match action {
        DepositAction::Create(args) => {
            super::validate_positive_amount(&args.amount, "amount")?;
            super::validate_address(&args.provider, "provider")?;
            let now = SystemTime::now()
                .duration_since(UNIX_EPOCH)
                .map_err(|_| anyhow::anyhow!("system clock error: time before UNIX epoch"))?
                .as_secs();
            let expiry = now as i64 + args.expiry as i64;
            let permit_sig = permit::auto_permit(&mut client, &args.amount, "deposit").await?;
            let deposit = client
                .deposit_create(&args.provider, &args.amount, expiry, Some(&permit_sig))
                .await?;
            if ctx.json {
                output::print_json(&deposit);
            } else {
                output::success(&format!("Deposit created: {}", deposit.id));
                output::print_kv(&[
                    ("ID", deposit.id.as_str()),
                    ("Status", deposit.status.as_str()),
                    ("Provider", deposit.provider.as_deref().unwrap_or("—")),
                    (
                        "Amount",
                        &deposit
                            .amount
                            .as_ref()
                            .map(|v| format!("{v} USDC"))
                            .unwrap_or_else(|| "—".to_string()),
                    ),
                    ("Expiry", deposit.expiry.as_deref().unwrap_or("—")),
                    ("Tx Hash", deposit.tx_hash.as_deref().unwrap_or("—")),
                ]);
            }
        }

        DepositAction::Return(args) => {
            let deposit = client.return_deposit(&args.deposit_id).await?;
            if ctx.json {
                output::print_json(&deposit);
            } else {
                output::success(&format!("Deposit {} returned", args.deposit_id));
                output::print_kv(&[
                    ("ID", deposit.id.as_str()),
                    ("Status", deposit.status.as_str()),
                    ("Provider", deposit.provider.as_deref().unwrap_or("—")),
                    (
                        "Amount",
                        &deposit
                            .amount
                            .as_ref()
                            .map(|v| format!("{v} USDC"))
                            .unwrap_or_else(|| "—".to_string()),
                    ),
                    ("Tx Hash", deposit.tx_hash.as_deref().unwrap_or("—")),
                ]);
            }
        }

        DepositAction::Forfeit(args) => {
            let deposit = client.forfeit_deposit(&args.deposit_id).await?;
            if ctx.json {
                output::print_json(&deposit);
            } else {
                output::success(&format!("Deposit {} forfeited", args.deposit_id));
                output::print_kv(&[
                    ("ID", deposit.id.as_str()),
                    ("Status", deposit.status.as_str()),
                    ("Provider", deposit.provider.as_deref().unwrap_or("—")),
                    (
                        "Amount",
                        &deposit
                            .amount
                            .as_ref()
                            .map(|v| format!("{v} USDC"))
                            .unwrap_or_else(|| "—".to_string()),
                    ),
                    ("Tx Hash", deposit.tx_hash.as_deref().unwrap_or("—")),
                ]);
            }
        }
    }
    Ok(())
}
