//! Wallet API endpoints.
//!
//! REST endpoints for wallet operations including:
//! - Wallet creation and import
//! - Address generation
//! - Balance queries
//! - Transaction signing

use axum::{
    extract::{Path, Query, State},
    routing::{get, post},
    Json, Router,
};
use serde::{Deserialize, Serialize};

use crate::{
    auth::{require_permission, Authenticated},
    error::{ApiError, ApiResult},
    response::{ApiResponse, PaginationParams},
    routes::AppState,
};

/// Build wallet routes.
pub fn router() -> Router<AppState> {
    Router::new()
        // Wallet management
        .route("/create", post(create_wallet))
        .route("/import", post(import_wallet))
        .route("/export/:address", get(export_mnemonic))
        // Address operations
        .route("/address", get(get_address))
        .route("/addresses", get(list_addresses))
        .route("/stealth-address", post(generate_stealth_address))
        // Balance queries
        .route("/balance/:address", get(get_balance))
        .route("/balances", post(get_balances))
        .route("/utxos/:address", get(get_utxos))
        // Transaction operations
        .route("/sign", post(sign_transaction))
        .route("/sign-message", post(sign_message))
        .route("/send", post(send_transaction))
}

// ============================================================================
// Request/Response Types
// ============================================================================

/// Request to create a new wallet.
#[derive(Debug, Serialize, Deserialize)]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
pub struct CreateWalletRequest {
    /// Number of words in mnemonic (12, 15, 18, 21, or 24)
    #[serde(default = "default_word_count")]
    pub word_count: u8,

    /// Optional passphrase for additional security
    #[serde(default)]
    pub passphrase: Option<String>,

    /// Network to use (mainnet, testnet, devnet)
    #[serde(default = "default_network")]
    pub network: String,
}

fn default_word_count() -> u8 {
    24
}

fn default_network() -> String {
    "mainnet".to_string()
}

/// Response for wallet creation.
#[derive(Debug, Serialize, Deserialize)]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
pub struct CreateWalletResponse {
    /// The generated mnemonic phrase (SAVE THIS SECURELY!)
    pub mnemonic: String,

    /// Primary address derived from the wallet
    pub address: String,

    /// Public key (hex encoded)
    pub public_key: String,

    /// Network the wallet was created for
    pub network: String,
}

/// Request to import an existing wallet.
#[derive(Debug, Serialize, Deserialize)]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
pub struct ImportWalletRequest {
    /// BIP-39 mnemonic phrase
    pub mnemonic: String,

    /// Optional passphrase
    #[serde(default)]
    pub passphrase: Option<String>,

    /// Network to use
    #[serde(default = "default_network")]
    pub network: String,
}

/// Response for wallet import.
#[derive(Debug, Serialize, Deserialize)]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
pub struct ImportWalletResponse {
    /// Primary address derived from the wallet
    pub address: String,

    /// Public key (hex encoded)
    pub public_key: String,

    /// Network the wallet is configured for
    pub network: String,

    /// Validation status
    pub valid: bool,
}

/// Query params for address derivation.
#[derive(Debug, Deserialize)]
pub struct AddressQuery {
    /// Account index (default: 0)
    #[serde(default)]
    pub account: u32,

    /// Address index (default: 0)
    #[serde(default)]
    pub index: u32,

    /// External (0) or internal/change (1)
    #[serde(default)]
    pub change: u32,
}

/// Balance response.
#[derive(Debug, Serialize, Deserialize)]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
pub struct BalanceResponse {
    /// Address queried
    pub address: String,

    /// Total balance (including unconfirmed)
    pub total: String,

    /// Confirmed balance
    pub confirmed: String,

    /// Unconfirmed balance
    pub unconfirmed: String,

    /// Balance in smallest unit
    pub balance_raw: String,

    /// Human-readable balance
    pub balance_formatted: String,
}

/// Request for multiple balances.
#[derive(Debug, Deserialize)]
pub struct GetBalancesRequest {
    /// List of addresses to query
    pub addresses: Vec<String>,
}

/// UTXO (Unspent Transaction Output).
#[derive(Debug, Serialize, Deserialize)]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
pub struct Utxo {
    /// Transaction ID
    pub txid: String,

    /// Output index
    pub vout: u32,

    /// Value in smallest unit
    pub value: String,

    /// Block height (None if unconfirmed)
    pub height: Option<u64>,

    /// Number of confirmations
    pub confirmations: u64,

    /// Script pubkey (hex)
    pub script_pubkey: String,
}

/// Request to sign a transaction.
#[derive(Debug, Deserialize)]
pub struct SignTransactionRequest {
    /// Raw transaction to sign (hex or JSON)
    pub transaction: serde_json::Value,

    /// Derivation path or address to sign with
    pub signer: String,
}

/// Response from signing.
#[derive(Debug, Serialize)]
pub struct SignTransactionResponse {
    /// Signed transaction (hex)
    pub signed_tx: String,

    /// Transaction ID
    pub txid: String,

    /// Signature (hex)
    pub signature: String,
}

/// Request to sign a message.
#[derive(Debug, Deserialize)]
pub struct SignMessageRequest {
    /// Message to sign
    pub message: String,

    /// Address to sign with
    pub address: String,
}

/// Response from message signing.
#[derive(Debug, Serialize)]
pub struct SignMessageResponse {
    /// Original message
    pub message: String,

    /// Signature (hex)
    pub signature: String,

    /// Public key used
    pub public_key: String,
}

/// Request to send a transaction.
#[derive(Debug, Deserialize)]
pub struct SendTransactionRequest {
    /// Recipient address
    pub to: String,

    /// Amount to send
    pub amount: String,

    /// Optional memo/note
    #[serde(default)]
    pub memo: Option<String>,

    /// Fee priority (low, medium, high)
    #[serde(default = "default_priority")]
    pub priority: String,
}

fn default_priority() -> String {
    "medium".to_string()
}

/// Response from sending.
#[derive(Debug, Serialize)]
pub struct SendTransactionResponse {
    /// Transaction ID
    pub txid: String,

    /// Amount sent
    pub amount: String,

    /// Fee paid
    pub fee: String,

    /// Transaction status
    pub status: String,
}

// ============================================================================
// Route Handlers
// ============================================================================

/// Create a new wallet with a generated mnemonic.
#[cfg_attr(feature = "openapi", utoipa::path(
    post,
    path = "/v1/wallet/create",
    request_body = CreateWalletRequest,
    responses(
        (status = 201, description = "Wallet created successfully", body = ApiResponse<CreateWalletResponse>),
        (status = 400, description = "Invalid request"),
        (status = 401, description = "Authentication required")
    ),
    security(("api_key" = [])),
    tag = "Wallet"
))]
async fn create_wallet(
    State(state): State<AppState>,
    Authenticated(auth): Authenticated,
    Json(req): Json<CreateWalletRequest>,
) -> ApiResult<Json<ApiResponse<CreateWalletResponse>>> {
    require_permission(&auth, "write")?;

    // Validate word count
    if ![12, 15, 18, 21, 24].contains(&req.word_count) {
        return Err(ApiError::ValidationError(
            "word_count must be 12, 15, 18, 21, or 24".to_string(),
        ));
    }

    // In production, this would call the crypto service
    // For now, return a placeholder
    let response = CreateWalletResponse {
        mnemonic: "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about".to_string(),
        address: "synor1abc123...".to_string(),
        public_key: "0x04...".to_string(),
        network: req.network,
    };

    Ok(Json(ApiResponse::success(response)))
}

/// Import a wallet from mnemonic.
#[cfg_attr(feature = "openapi", utoipa::path(
    post,
    path = "/v1/wallet/import",
    request_body = ImportWalletRequest,
    responses(
        (status = 200, description = "Wallet imported successfully", body = ApiResponse<ImportWalletResponse>),
        (status = 400, description = "Invalid mnemonic"),
        (status = 401, description = "Authentication required")
    ),
    security(("api_key" = [])),
    tag = "Wallet"
))]
async fn import_wallet(
    State(state): State<AppState>,
    Authenticated(auth): Authenticated,
    Json(req): Json<ImportWalletRequest>,
) -> ApiResult<Json<ApiResponse<ImportWalletResponse>>> {
    require_permission(&auth, "write")?;

    // Validate mnemonic word count
    let word_count = req.mnemonic.split_whitespace().count();
    if ![12, 15, 18, 21, 24].contains(&word_count) {
        return Err(ApiError::ValidationError(
            "Invalid mnemonic: must be 12, 15, 18, 21, or 24 words".to_string(),
        ));
    }

    // In production, validate mnemonic checksum and derive keys
    let response = ImportWalletResponse {
        address: "synor1xyz789...".to_string(),
        public_key: "0x04...".to_string(),
        network: req.network,
        valid: true,
    };

    Ok(Json(ApiResponse::success(response)))
}

/// Export mnemonic for an address (requires admin permission).
async fn export_mnemonic(
    State(state): State<AppState>,
    Authenticated(auth): Authenticated,
    Path(address): Path<String>,
) -> ApiResult<Json<ApiResponse<serde_json::Value>>> {
    require_permission(&auth, "admin")?;

    // This is a sensitive operation that requires additional security
    Err(ApiError::Forbidden(
        "Mnemonic export requires additional verification".to_string(),
    ))
}

/// Get address at derivation path.
async fn get_address(
    State(state): State<AppState>,
    Authenticated(auth): Authenticated,
    Query(query): Query<AddressQuery>,
) -> ApiResult<Json<ApiResponse<serde_json::Value>>> {
    require_permission(&auth, "read")?;

    let address = serde_json::json!({
        "address": "synor1derived...",
        "path": format!("m/44'/21337'/{}'/{}'/{}",
            query.account, query.change, query.index),
        "public_key": "0x04..."
    });

    Ok(Json(ApiResponse::success(address)))
}

/// List all derived addresses.
async fn list_addresses(
    State(state): State<AppState>,
    Authenticated(auth): Authenticated,
    Query(pagination): Query<PaginationParams>,
) -> ApiResult<Json<ApiResponse<Vec<serde_json::Value>>>> {
    require_permission(&auth, "read")?;

    let addresses = vec![
        serde_json::json!({
            "address": "synor1addr1...",
            "index": 0,
            "balance": "100.00"
        }),
        serde_json::json!({
            "address": "synor1addr2...",
            "index": 1,
            "balance": "50.00"
        }),
    ];

    let pagination_meta = pagination.to_meta(addresses.len() as u64);
    Ok(Json(ApiResponse::success_paginated(addresses, pagination_meta)))
}

/// Generate a stealth address.
async fn generate_stealth_address(
    State(state): State<AppState>,
    Authenticated(auth): Authenticated,
) -> ApiResult<Json<ApiResponse<serde_json::Value>>> {
    require_permission(&auth, "write")?;

    let stealth = serde_json::json!({
        "stealth_address": "synor1stealth...",
        "scan_key": "0x...",
        "spend_key": "0x..."
    });

    Ok(Json(ApiResponse::success(stealth)))
}

/// Get balance for an address.
#[cfg_attr(feature = "openapi", utoipa::path(
    get,
    path = "/v1/wallet/balance/{address}",
    params(
        ("address" = String, Path, description = "Wallet address")
    ),
    responses(
        (status = 200, description = "Balance retrieved", body = ApiResponse<BalanceResponse>),
        (status = 400, description = "Invalid address"),
        (status = 404, description = "Address not found")
    ),
    security(("api_key" = [])),
    tag = "Wallet"
))]
async fn get_balance(
    State(state): State<AppState>,
    Authenticated(auth): Authenticated,
    Path(address): Path<String>,
) -> ApiResult<Json<ApiResponse<BalanceResponse>>> {
    require_permission(&auth, "read")?;

    // Validate address format
    if !address.starts_with("synor1") {
        return Err(ApiError::InvalidAddress(address));
    }

    // In production, query the RPC service
    let balance = BalanceResponse {
        address,
        total: "150.50".to_string(),
        confirmed: "150.00".to_string(),
        unconfirmed: "0.50".to_string(),
        balance_raw: "150500000000".to_string(),
        balance_formatted: "150.50 SYNOR".to_string(),
    };

    Ok(Json(ApiResponse::success(balance)))
}

/// Get balances for multiple addresses.
async fn get_balances(
    State(state): State<AppState>,
    Authenticated(auth): Authenticated,
    Json(req): Json<GetBalancesRequest>,
) -> ApiResult<Json<ApiResponse<Vec<BalanceResponse>>>> {
    require_permission(&auth, "read")?;

    if req.addresses.is_empty() {
        return Err(ApiError::ValidationError("addresses cannot be empty".to_string()));
    }

    if req.addresses.len() > 100 {
        return Err(ApiError::ValidationError(
            "Maximum 100 addresses per request".to_string(),
        ));
    }

    // In production, batch query the RPC service
    let balances: Vec<BalanceResponse> = req
        .addresses
        .iter()
        .map(|addr| BalanceResponse {
            address: addr.clone(),
            total: "100.00".to_string(),
            confirmed: "100.00".to_string(),
            unconfirmed: "0.00".to_string(),
            balance_raw: "100000000000".to_string(),
            balance_formatted: "100.00 SYNOR".to_string(),
        })
        .collect();

    Ok(Json(ApiResponse::success(balances)))
}

/// Get UTXOs for an address.
async fn get_utxos(
    State(state): State<AppState>,
    Authenticated(auth): Authenticated,
    Path(address): Path<String>,
    Query(pagination): Query<PaginationParams>,
) -> ApiResult<Json<ApiResponse<Vec<Utxo>>>> {
    require_permission(&auth, "read")?;

    // In production, query the RPC service
    let utxos = vec![
        Utxo {
            txid: "abc123...".to_string(),
            vout: 0,
            value: "50000000000".to_string(),
            height: Some(100000),
            confirmations: 100,
            script_pubkey: "76a914...".to_string(),
        },
        Utxo {
            txid: "def456...".to_string(),
            vout: 1,
            value: "100500000000".to_string(),
            height: Some(100050),
            confirmations: 50,
            script_pubkey: "76a914...".to_string(),
        },
    ];

    let pagination_meta = pagination.to_meta(utxos.len() as u64);
    Ok(Json(ApiResponse::success_paginated(utxos, pagination_meta)))
}

/// Sign a transaction.
async fn sign_transaction(
    State(state): State<AppState>,
    Authenticated(auth): Authenticated,
    Json(req): Json<SignTransactionRequest>,
) -> ApiResult<Json<ApiResponse<SignTransactionResponse>>> {
    require_permission(&auth, "write")?;

    // In production, this would use the crypto service
    let response = SignTransactionResponse {
        signed_tx: "0x...signed...".to_string(),
        txid: "txid123...".to_string(),
        signature: "0x...signature...".to_string(),
    };

    Ok(Json(ApiResponse::success(response)))
}

/// Sign a message.
async fn sign_message(
    State(state): State<AppState>,
    Authenticated(auth): Authenticated,
    Json(req): Json<SignMessageRequest>,
) -> ApiResult<Json<ApiResponse<SignMessageResponse>>> {
    require_permission(&auth, "write")?;

    let response = SignMessageResponse {
        message: req.message,
        signature: "0x...message_signature...".to_string(),
        public_key: "0x04...".to_string(),
    };

    Ok(Json(ApiResponse::success(response)))
}

/// Send a transaction.
async fn send_transaction(
    State(state): State<AppState>,
    Authenticated(auth): Authenticated,
    Json(req): Json<SendTransactionRequest>,
) -> ApiResult<Json<ApiResponse<SendTransactionResponse>>> {
    require_permission(&auth, "write")?;

    // Validate recipient address
    if !req.to.starts_with("synor1") {
        return Err(ApiError::InvalidAddress(req.to));
    }

    // Validate amount
    let amount: f64 = req.amount.parse().map_err(|_| {
        ApiError::ValidationError("Invalid amount format".to_string())
    })?;

    if amount <= 0.0 {
        return Err(ApiError::ValidationError("Amount must be positive".to_string()));
    }

    // In production, build, sign, and broadcast the transaction
    let response = SendTransactionResponse {
        txid: "newtxid789...".to_string(),
        amount: req.amount,
        fee: "0.001".to_string(),
        status: "pending".to_string(),
    };

    Ok(Json(ApiResponse::success(response)))
}
