use alloy::primitives::{Address, Bytes};
use clap::{Parser, Subcommand};
use eyre::Context;
use newton_prover_chainio::policy_client::PolicyClientController;
use newton_prover_core::config::NewtonAvsConfig;
use std::path::PathBuf;
use tracing;

use crate::config::NewtonCliConfig;

/// Policy client commands
#[derive(Debug, Parser)]
#[command(name = "policy-client")]
pub struct PolicyClientCommand {
    #[command(subcommand)]
    pub subcommand: PolicyClientSubcommand,
}

#[derive(Debug, Subcommand)]
pub enum PolicyClientSubcommand {
    /// Set policy parameters for a policy client
    #[command(name = "set-policy-params")]
    SetPolicyParams(SetPolicyParamsCommand),
}

/// Set policy parameters command
#[derive(Debug, Parser)]
pub struct SetPolicyParamsCommand {
    /// Policy client address
    #[arg(long)]
    policy_client: Address,

    /// Path to JSON file containing policy parameters
    #[arg(long)]
    policy_params: PathBuf,

    /// Expire after (in blocks)
    #[arg(long)]
    expire_after: u32,

    #[arg(long, env = "PRIVATE_KEY")]
    private_key: Option<String>,

    #[arg(long, env = "RPC_URL")]
    rpc_url: Option<String>,
}

impl PolicyClientCommand {
    /// Execute the policy-client command
    pub async fn execute(self: Box<Self>, _config: NewtonAvsConfig<NewtonCliConfig>) -> eyre::Result<()> {
        match self.subcommand {
            PolicyClientSubcommand::SetPolicyParams(cmd) => {
                let private_key = cmd
                    .private_key
                    .ok_or_else(|| eyre::eyre!("PRIVATE_KEY is required (set via --private-key or env var)"))?;
                let rpc_url = cmd
                    .rpc_url
                    .ok_or_else(|| eyre::eyre!("RPC_URL is required (set via --rpc-url or env var)"))?;

                tracing::info!("Setting policy params for policy client: {}", cmd.policy_client);

                // Read policy params JSON file
                let policy_params_str = std::fs::read_to_string(&cmd.policy_params)
                    .with_context(|| format!("Failed to read policy params file: {:?}", cmd.policy_params))?;

                // Validate it's valid JSON
                let _policy_params_json: serde_json::Value = serde_json::from_str(&policy_params_str)
                    .with_context(|| format!("Failed to parse policy params as JSON: {:?}", cmd.policy_params))?;

                // Convert to bytes
                let policy_params_bytes = Bytes::copy_from_slice(policy_params_str.as_bytes());

                // Create controller and set policy
                let controller = PolicyClientController::new(private_key, rpc_url, cmd.policy_client);
                let (receipt, policy_id) = controller
                    .set_policy(policy_params_bytes, cmd.expire_after)
                    .await
                    .map_err(|e| eyre::eyre!("Failed to set policy: {}", e))?;

                tracing::info!("Policy set successfully!");
                tracing::info!("Transaction hash: {:?}", receipt.transaction_hash);
                tracing::info!("Policy ID: {:?}", policy_id);

                Ok(())
            }
        }
    }
}
