use crate::prelude::*;
use nostr_sdk::{PublicKey, Timestamp};
use serde::{Deserialize, Serialize};
#[cfg(feature = "sqlx")]
use sqlx::FromRow;
#[cfg(feature = "sqlx")]
use sqlx_crud::SqlxCrud;
use std::{fmt::Display, str::FromStr};
use uuid::Uuid;
use wasm_bindgen::prelude::*;

/// Orders can be only Buy or Sell
#[wasm_bindgen]
#[derive(Debug, Deserialize, Serialize, Clone, Copy, PartialEq, Eq)]
#[serde(rename_all = "kebab-case")]
pub enum Kind {
    Buy,
    Sell,
}

impl FromStr for Kind {
    type Err = ();

    fn from_str(kind: &str) -> std::result::Result<Self, Self::Err> {
        match kind.to_lowercase().as_str() {
            "buy" => std::result::Result::Ok(Self::Buy),
            "sell" => std::result::Result::Ok(Self::Sell),
            _ => Err(()),
        }
    }
}

impl Display for Kind {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Kind::Sell => write!(f, "sell"),
            Kind::Buy => write!(f, "buy"),
        }
    }
}

/// Each status that an order can have
#[wasm_bindgen]
#[derive(Debug, Deserialize, Serialize, Clone, Copy, PartialEq, Eq)]
#[serde(rename_all = "kebab-case")]
pub enum Status {
    Active,
    Canceled,
    CanceledByAdmin,
    SettledByAdmin,
    CompletedByAdmin,
    Dispute,
    Expired,
    FiatSent,
    SettledHoldInvoice,
    Pending,
    Success,
    WaitingBuyerInvoice,
    WaitingPayment,
    CooperativelyCanceled,
    InProgress,
}

impl Display for Status {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Status::Active => write!(f, "active"),
            Status::Canceled => write!(f, "canceled"),
            Status::CanceledByAdmin => write!(f, "canceled-by-admin"),
            Status::SettledByAdmin => write!(f, "settled-by-admin"),
            Status::CompletedByAdmin => write!(f, "completed-by-admin"),
            Status::Dispute => write!(f, "dispute"),
            Status::Expired => write!(f, "expired"),
            Status::FiatSent => write!(f, "fiat-sent"),
            Status::SettledHoldInvoice => write!(f, "settled-hold-invoice"),
            Status::Pending => write!(f, "pending"),
            Status::Success => write!(f, "success"),
            Status::WaitingBuyerInvoice => write!(f, "waiting-buyer-invoice"),
            Status::WaitingPayment => write!(f, "waiting-payment"),
            Status::CooperativelyCanceled => write!(f, "cooperatively-canceled"),
            Status::InProgress => write!(f, "in-progress"),
        }
    }
}

impl FromStr for Status {
    type Err = ();
    /// Convert a string to a status
    fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
        match s.to_lowercase().as_str() {
            "active" => std::result::Result::Ok(Self::Active),
            "canceled" => std::result::Result::Ok(Self::Canceled),
            "canceled-by-admin" => std::result::Result::Ok(Self::CanceledByAdmin),
            "settled-by-admin" => std::result::Result::Ok(Self::SettledByAdmin),
            "completed-by-admin" => std::result::Result::Ok(Self::CompletedByAdmin),
            "dispute" => std::result::Result::Ok(Self::Dispute),
            "expired" => std::result::Result::Ok(Self::Expired),
            "fiat-sent" => std::result::Result::Ok(Self::FiatSent),
            "settled-hold-invoice" => std::result::Result::Ok(Self::SettledHoldInvoice),
            "pending" => std::result::Result::Ok(Self::Pending),
            "success" => std::result::Result::Ok(Self::Success),
            "waiting-buyer-invoice" => std::result::Result::Ok(Self::WaitingBuyerInvoice),
            "waiting-payment" => std::result::Result::Ok(Self::WaitingPayment),
            "cooperatively-canceled" => std::result::Result::Ok(Self::CooperativelyCanceled),
            "in-progress" => std::result::Result::Ok(Self::InProgress),
            _ => Err(()),
        }
    }
}
/// Database representation of an order
#[cfg_attr(feature = "sqlx", derive(FromRow, SqlxCrud), external_id)]
#[derive(Debug, Default, Deserialize, Serialize, Clone)]
pub struct Order {
    pub id: Uuid,
    pub kind: String,
    pub event_id: String,
    pub hash: Option<String>,
    pub preimage: Option<String>,
    pub creator_pubkey: String,
    pub cancel_initiator_pubkey: Option<String>,
    pub buyer_pubkey: Option<String>,
    pub master_buyer_pubkey: Option<String>,
    pub seller_pubkey: Option<String>,
    pub master_seller_pubkey: Option<String>,
    pub status: String,
    pub price_from_api: bool,
    pub premium: i64,
    pub payment_method: String,
    pub amount: i64,
    pub min_amount: Option<i64>,
    pub max_amount: Option<i64>,
    pub buyer_dispute: bool,
    pub seller_dispute: bool,
    pub buyer_cooperativecancel: bool,
    pub seller_cooperativecancel: bool,
    pub fee: i64,
    pub routing_fee: i64,
    pub dev_fee: i64,
    pub dev_fee_paid: bool,
    pub dev_fee_payment_hash: Option<String>,
    pub fiat_code: String,
    pub fiat_amount: i64,
    pub buyer_invoice: Option<String>,
    pub range_parent_id: Option<Uuid>,
    pub invoice_held_at: i64,
    pub taken_at: i64,
    pub created_at: i64,
    pub buyer_sent_rate: bool,
    pub seller_sent_rate: bool,
    pub failed_payment: bool,
    pub payment_attempts: i64,
    pub expires_at: i64,
    pub trade_index_seller: Option<i64>,
    pub trade_index_buyer: Option<i64>,
    pub next_trade_pubkey: Option<String>,
    pub next_trade_index: Option<i64>,
}

impl From<SmallOrder> for Order {
    fn from(small_order: SmallOrder) -> Self {
        Self {
            id: Uuid::new_v4(),
            // order will be overwritten with the real one before publishing
            kind: small_order
                .kind
                .map_or_else(|| Kind::Buy.to_string(), |k| k.to_string()),
            status: small_order
                .status
                .map_or_else(|| Status::Active.to_string(), |s| s.to_string()),
            amount: small_order.amount,
            fiat_code: small_order.fiat_code,
            min_amount: small_order.min_amount,
            max_amount: small_order.max_amount,
            fiat_amount: small_order.fiat_amount,
            payment_method: small_order.payment_method,
            premium: small_order.premium,
            event_id: String::new(),
            creator_pubkey: String::new(),
            price_from_api: false,
            fee: 0,
            routing_fee: 0,
            dev_fee: 0,
            dev_fee_paid: false,
            dev_fee_payment_hash: None,
            invoice_held_at: 0,
            taken_at: 0,
            created_at: small_order.created_at.unwrap_or(0),
            expires_at: small_order.expires_at.unwrap_or(0),
            payment_attempts: 0,
            ..Default::default()
        }
    }
}

impl Order {
    /// Convert an order to a small order
    pub fn as_new_order(&self) -> SmallOrder {
        SmallOrder::new(
            Some(self.id),
            Some(Kind::from_str(&self.kind).unwrap()),
            Some(Status::from_str(&self.status).unwrap()),
            self.amount,
            self.fiat_code.clone(),
            self.min_amount,
            self.max_amount,
            self.fiat_amount,
            self.payment_method.clone(),
            self.premium,
            None,
            None,
            self.buyer_invoice.clone(),
            Some(self.created_at),
            Some(self.expires_at),
        )
    }
    /// Get the kind of the order
    pub fn get_order_kind(&self) -> Result<Kind, ServiceError> {
        if let Ok(kind) = Kind::from_str(&self.kind) {
            Ok(kind)
        } else {
            Err(ServiceError::InvalidOrderKind)
        }
    }

    /// Get the status of the order in case
    pub fn get_order_status(&self) -> Result<Status, ServiceError> {
        if let Ok(status) = Status::from_str(&self.status) {
            Ok(status)
        } else {
            Err(ServiceError::InvalidOrderStatus)
        }
    }

    /// Compare the status of the order
    pub fn check_status(&self, status: Status) -> Result<(), CantDoReason> {
        match Status::from_str(&self.status) {
            Ok(s) => match s == status {
                true => Ok(()),
                false => Err(CantDoReason::InvalidOrderStatus),
            },
            Err(_) => Err(CantDoReason::InvalidOrderStatus),
        }
    }

    /// Check if the order is a buy order
    pub fn is_buy_order(&self) -> Result<(), CantDoReason> {
        if self.kind != Kind::Buy.to_string() {
            return Err(CantDoReason::InvalidOrderKind);
        }
        Ok(())
    }
    /// Check if the order is a sell order
    pub fn is_sell_order(&self) -> Result<(), CantDoReason> {
        if self.kind != Kind::Sell.to_string() {
            return Err(CantDoReason::InvalidOrderKind);
        }
        Ok(())
    }

    /// Check if the sender is the creator of the order
    pub fn sent_from_maker(&self, sender: PublicKey) -> Result<(), CantDoReason> {
        let sender = sender.to_string();
        if self.creator_pubkey != sender {
            return Err(CantDoReason::InvalidPubkey);
        }
        Ok(())
    }

    /// Check if the sender is the creator of the order
    pub fn not_sent_from_maker(&self, sender: PublicKey) -> Result<(), CantDoReason> {
        let sender = sender.to_string();
        if self.creator_pubkey == sender {
            return Err(CantDoReason::InvalidPubkey);
        }
        Ok(())
    }

    /// Get the creator pubkey
    pub fn get_creator_pubkey(&self) -> Result<PublicKey, ServiceError> {
        match PublicKey::from_str(self.creator_pubkey.as_ref()) {
            Ok(pk) => Ok(pk),
            Err(_) => Err(ServiceError::InvalidPubkey),
        }
    }

    /// Get the buyer pubkey
    pub fn get_buyer_pubkey(&self) -> Result<PublicKey, ServiceError> {
        if let Some(pk) = self.buyer_pubkey.as_ref() {
            PublicKey::from_str(pk).map_err(|_| ServiceError::InvalidPubkey)
        } else {
            Err(ServiceError::InvalidPubkey)
        }
    }
    /// Get the seller pubkey
    pub fn get_seller_pubkey(&self) -> Result<PublicKey, ServiceError> {
        if let Some(pk) = self.seller_pubkey.as_ref() {
            PublicKey::from_str(pk).map_err(|_| ServiceError::InvalidPubkey)
        } else {
            Err(ServiceError::InvalidPubkey)
        }
    }
    /// Get the master buyer pubkey
    pub fn get_master_buyer_pubkey(&self) -> Result<PublicKey, ServiceError> {
        if let Some(pk) = self.master_buyer_pubkey.as_ref() {
            PublicKey::from_str(pk).map_err(|_| ServiceError::InvalidPubkey)
        } else {
            Err(ServiceError::InvalidPubkey)
        }
    }
    /// Get the master seller pubkey
    pub fn get_master_seller_pubkey(&self) -> Result<PublicKey, ServiceError> {
        if let Some(pk) = self.master_seller_pubkey.as_ref() {
            PublicKey::from_str(pk).map_err(|_| ServiceError::InvalidPubkey)
        } else {
            Err(ServiceError::InvalidPubkey)
        }
    }

    /// Check if the order is a range order
    pub fn is_range_order(&self) -> bool {
        self.min_amount.is_some() && self.max_amount.is_some()
    }

    pub fn count_failed_payment(&mut self, retries_number: i64) {
        if !self.failed_payment {
            self.failed_payment = true;
            self.payment_attempts = 1;
        } else if self.payment_attempts < retries_number {
            self.payment_attempts += 1;
        }
    }

    /// Check if the order has no amount
    pub fn has_no_amount(&self) -> bool {
        self.amount == 0
    }

    /// Set the timestamp to now
    pub fn set_timestamp_now(&mut self) {
        self.taken_at = Timestamp::now().as_u64() as i64
    }

    /// Check if a user is creating a full privacy order so he doesn't to have reputation
    /// compare master keys with the order keys if they are the same the user is in full privacy mode
    /// otherwise the user is not in normal mode and has a reputation
    pub fn is_full_privacy_order(&self) -> Result<(Option<String>, Option<String>), ServiceError> {
        let (mut normal_buyer_idkey, mut normal_seller_idkey) = (None, None);

        // Get master pubkeys to get users data from db
        let master_buyer_pubkey = self.get_master_buyer_pubkey().ok();
        let master_seller_pubkey = self.get_master_seller_pubkey().ok();

        // Check if the buyer is in full privacy mode
        if self.buyer_pubkey != master_buyer_pubkey.map(|pk| pk.to_string()) {
            normal_buyer_idkey = master_buyer_pubkey.map(|pk| pk.to_string());
        }

        // Check if the seller is in full privacy mode
        if self.seller_pubkey != master_seller_pubkey.map(|pk| pk.to_string()) {
            normal_seller_idkey = master_seller_pubkey.map(|pk| pk.to_string());
        }

        Ok((normal_buyer_idkey, normal_seller_idkey))
    }
    /// Setup the dispute status
    ///
    /// If the pubkey is the buyer, set the buyer dispute to true
    /// If the pubkey is the seller, set the seller dispute to true
    pub fn setup_dispute(&mut self, is_buyer_dispute: bool) -> Result<(), CantDoReason> {
        // Get the opposite dispute status
        let is_seller_dispute = !is_buyer_dispute;

        // Update dispute flags based on who initiated
        let mut update_seller_dispute = false;
        let mut update_buyer_dispute = false;

        if is_seller_dispute && !self.seller_dispute {
            update_seller_dispute = true;
            self.seller_dispute = update_seller_dispute;
        } else if is_buyer_dispute && !self.buyer_dispute {
            update_buyer_dispute = true;
            self.buyer_dispute = update_buyer_dispute;
        };
        // Set the status to dispute
        self.status = Status::Dispute.to_string();

        // Update the database with dispute information
        // Save the dispute to DB
        if !update_buyer_dispute && !update_seller_dispute {
            return Err(CantDoReason::DisputeCreationError);
        }

        Ok(())
    }
}

/// We use this struct to create a new order
#[derive(Debug, Default, Deserialize, Serialize, Clone)]
#[serde(deny_unknown_fields)]
pub struct SmallOrder {
    #[serde(skip_serializing_if = "Option::is_none")]
    pub id: Option<Uuid>,
    pub kind: Option<Kind>,
    pub status: Option<Status>,
    pub amount: i64,
    pub fiat_code: String,
    pub min_amount: Option<i64>,
    pub max_amount: Option<i64>,
    pub fiat_amount: i64,
    pub payment_method: String,
    pub premium: i64,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub buyer_trade_pubkey: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub seller_trade_pubkey: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub buyer_invoice: Option<String>,
    pub created_at: Option<i64>,
    pub expires_at: Option<i64>,
}

#[allow(dead_code)]
impl SmallOrder {
    #[allow(clippy::too_many_arguments)]
    pub fn new(
        id: Option<Uuid>,
        kind: Option<Kind>,
        status: Option<Status>,
        amount: i64,
        fiat_code: String,
        min_amount: Option<i64>,
        max_amount: Option<i64>,
        fiat_amount: i64,
        payment_method: String,
        premium: i64,
        buyer_trade_pubkey: Option<String>,
        seller_trade_pubkey: Option<String>,
        buyer_invoice: Option<String>,
        created_at: Option<i64>,
        expires_at: Option<i64>,
    ) -> Self {
        Self {
            id,
            kind,
            status,
            amount,
            fiat_code,
            min_amount,
            max_amount,
            fiat_amount,
            payment_method,
            premium,
            buyer_trade_pubkey,
            seller_trade_pubkey,
            buyer_invoice,
            created_at,
            expires_at,
        }
    }
    /// New order from json string
    pub fn from_json(json: &str) -> Result<Self, ServiceError> {
        serde_json::from_str(json).map_err(|_| ServiceError::MessageSerializationError)
    }

    /// Get order as json string
    pub fn as_json(&self) -> Result<String, ServiceError> {
        serde_json::to_string(&self).map_err(|_| ServiceError::MessageSerializationError)
    }

    /// Get the amount of sats or the string "Market price"
    pub fn sats_amount(&self) -> String {
        if self.amount == 0 {
            "Market price".to_string()
        } else {
            self.amount.to_string()
        }
    }
    /// Check if fiat_amount is positive
    pub fn check_fiat_amount(&self) -> Result<(), CantDoReason> {
        if self.fiat_amount <= 0 {
            return Err(CantDoReason::InvalidAmount);
        }
        Ok(())
    }

    /// Check if amount (sats) is non-negative when explicitly set
    pub fn check_amount(&self) -> Result<(), CantDoReason> {
        if self.amount < 0 {
            return Err(CantDoReason::InvalidAmount);
        }
        Ok(())
    }

    /// Check if the order has a zero amount and a premium or fiat amount
    pub fn check_zero_amount_with_premium(&self) -> Result<(), CantDoReason> {
        let premium = (self.premium != 0).then_some(self.premium);
        let sats_amount = (self.amount != 0).then_some(self.amount);

        if premium.is_some() && sats_amount.is_some() {
            return Err(CantDoReason::InvalidParameters);
        }
        Ok(())
    }

    /// Check if the order is a range order and if the amount is zero
    pub fn check_range_order_limits(&self, amounts: &mut Vec<i64>) -> Result<(), CantDoReason> {
        // Check if the min and max amount are valid and update the vector
        if let (Some(min), Some(max)) = (self.min_amount, self.max_amount) {
            if min < 0 || max < 0 {
                return Err(CantDoReason::InvalidAmount);
            }
            if min >= max {
                return Err(CantDoReason::InvalidAmount);
            }
            if self.amount != 0 {
                return Err(CantDoReason::InvalidAmount);
            }
            amounts.clear();
            amounts.push(min);
            amounts.push(max);
        }
        Ok(())
    }

    /// Check if the fiat currency is accepted
    pub fn check_fiat_currency(
        &self,
        fiat_currencies_accepted: &[String],
    ) -> Result<(), CantDoReason> {
        if !fiat_currencies_accepted.contains(&self.fiat_code)
            && !fiat_currencies_accepted.is_empty()
        {
            return Err(CantDoReason::InvalidFiatCurrency);
        }
        Ok(())
    }
}

impl From<Order> for SmallOrder {
    fn from(order: Order) -> Self {
        let id = Some(order.id);
        let kind = Kind::from_str(&order.kind).unwrap();
        let status = Status::from_str(&order.status).unwrap();
        let amount = order.amount;
        let fiat_code = order.fiat_code.clone();
        let min_amount = order.min_amount;
        let max_amount = order.max_amount;
        let fiat_amount = order.fiat_amount;
        let payment_method = order.payment_method.clone();
        let premium = order.premium;
        let buyer_trade_pubkey = order.buyer_pubkey.clone();
        let seller_trade_pubkey = order.seller_pubkey.clone();
        let buyer_invoice = order.buyer_invoice.clone();

        Self {
            id,
            kind: Some(kind),
            status: Some(status),
            amount,
            fiat_code,
            min_amount,
            max_amount,
            fiat_amount,
            payment_method,
            premium,
            buyer_trade_pubkey,
            seller_trade_pubkey,
            buyer_invoice,
            created_at: Some(order.created_at),
            expires_at: Some(order.expires_at),
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::error::CantDoReason;
    use nostr_sdk::Keys;
    use uuid::uuid;

    #[test]
    fn test_status_string() {
        assert_eq!(Status::Active.to_string(), "active");
        assert_eq!(Status::CompletedByAdmin.to_string(), "completed-by-admin");
        assert_eq!(Status::FiatSent.to_string(), "fiat-sent");
        assert_ne!(Status::Pending.to_string(), "Pending");
    }

    #[test]
    fn test_kind_string() {
        assert_ne!(Kind::Sell.to_string(), "active");
        assert_eq!(Kind::Sell.to_string(), "sell");
        assert_eq!(Kind::Buy.to_string(), "buy");
        assert_ne!(Kind::Buy.to_string(), "active");
    }

    #[test]
    fn test_order_message() {
        let uuid = uuid!("308e1272-d5f4-47e6-bd97-3504baea9c23");
        let payment_methods = "SEPA,Bank transfer".to_string();
        let payload = Payload::Order(SmallOrder::new(
            Some(uuid),
            Some(Kind::Sell),
            Some(Status::Pending),
            100,
            "eur".to_string(),
            None,
            None,
            100,
            payment_methods,
            1,
            None,
            None,
            None,
            Some(1627371434),
            None,
        ));

        let test_message = Message::Order(MessageKind::new(
            Some(uuid),
            Some(1),
            Some(2),
            Action::NewOrder,
            Some(payload),
        ));
        let test_message_json = test_message.as_json().unwrap();
        let sample_message = r#"{"order":{"version":1,"id":"308e1272-d5f4-47e6-bd97-3504baea9c23","request_id":1,"trade_index":2,"action":"new-order","payload":{"order":{"id":"308e1272-d5f4-47e6-bd97-3504baea9c23","kind":"sell","status":"pending","amount":100,"fiat_code":"eur","fiat_amount":100,"payment_method":"SEPA,Bank transfer","premium":1,"created_at":1627371434}}}}"#;
        let message = Message::from_json(sample_message).unwrap();
        assert!(message.verify());
        let message_json = message.as_json().unwrap();
        assert_eq!(message_json, test_message_json);
    }

    #[test]
    fn test_payment_request_payload_message() {
        let uuid = uuid!("308e1272-d5f4-47e6-bd97-3504baea9c23");
        let test_message = Message::Order(MessageKind::new(
            Some(uuid),
            Some(1),
            Some(3),
            Action::PayInvoice,
            Some(Payload::PaymentRequest(
                Some(SmallOrder::new(
                    Some(uuid),
                    Some(Kind::Sell),
                    Some(Status::WaitingPayment),
                    100,
                    "eur".to_string(),
                    None,
                    None,
                    100,
                    "Face to face".to_string(),
                    1,
                    None,
                    None,
                    None,
                    Some(1627371434),
                    None,
                )),
                "lnbcrt78510n1pj59wmepp50677g8tffdqa2p8882y0x6newny5vtz0hjuyngdwv226nanv4uzsdqqcqzzsxqyz5vqsp5skn973360gp4yhlpmefwvul5hs58lkkl3u3ujvt57elmp4zugp4q9qyyssqw4nzlr72w28k4waycf27qvgzc9sp79sqlw83j56txltz4va44j7jda23ydcujj9y5k6k0rn5ms84w8wmcmcyk5g3mhpqepf7envhdccp72nz6e".to_string(),
                None,
            )),
        ));
        let sample_message = r#"{"order":{"version":1,"id":"308e1272-d5f4-47e6-bd97-3504baea9c23","request_id":1,"trade_index":3,"action":"pay-invoice","payload":{"payment_request":[{"id":"308e1272-d5f4-47e6-bd97-3504baea9c23","kind":"sell","status":"waiting-payment","amount":100,"fiat_code":"eur","fiat_amount":100,"payment_method":"Face to face","premium":1,"created_at":1627371434},"lnbcrt78510n1pj59wmepp50677g8tffdqa2p8882y0x6newny5vtz0hjuyngdwv226nanv4uzsdqqcqzzsxqyz5vqsp5skn973360gp4yhlpmefwvul5hs58lkkl3u3ujvt57elmp4zugp4q9qyyssqw4nzlr72w28k4waycf27qvgzc9sp79sqlw83j56txltz4va44j7jda23ydcujj9y5k6k0rn5ms84w8wmcmcyk5g3mhpqepf7envhdccp72nz6e",null]}}}"#;
        let message = Message::from_json(sample_message).unwrap();
        assert!(message.verify());
        let message_json = message.as_json().unwrap();
        let test_message_json = test_message.as_json().unwrap();
        assert_eq!(message_json, test_message_json);
    }

    #[test]
    fn test_message_payload_signature() {
        let uuid = uuid!("308e1272-d5f4-47e6-bd97-3504baea9c23");
        let peer = Peer::new(
            "npub1testjsf0runcqdht5apkfcalajxkf8txdxqqk5kgm0agc38ke4vsfsgzf8".to_string(),
            None,
        );
        let payload = Payload::Peer(peer);
        let test_message = Message::Order(MessageKind::new(
            Some(uuid),
            Some(1),
            Some(2),
            Action::FiatSentOk,
            Some(payload),
        ));
        assert!(test_message.verify());
        let test_message_json = test_message.as_json().unwrap();
        // Message should be signed with the trade keys
        let trade_keys =
            Keys::parse("110e43647eae221ab1da33ddc17fd6ff423f2b2f49d809b9ffa40794a2ab996c")
                .unwrap();
        let sig = Message::sign(test_message_json.clone(), &trade_keys);

        assert!(Message::verify_signature(
            test_message_json,
            trade_keys.public_key(),
            sig
        ));
    }

    #[test]
    fn test_cant_do_message_serialization() {
        // Test all CantDoReason variants
        let reasons = vec![
            CantDoReason::InvalidSignature,
            CantDoReason::InvalidTradeIndex,
            CantDoReason::InvalidAmount,
            CantDoReason::InvalidInvoice,
            CantDoReason::InvalidPaymentRequest,
            CantDoReason::InvalidPeer,
            CantDoReason::InvalidRating,
            CantDoReason::InvalidTextMessage,
            CantDoReason::InvalidOrderStatus,
            CantDoReason::InvalidPubkey,
            CantDoReason::InvalidParameters,
            CantDoReason::OrderAlreadyCanceled,
            CantDoReason::CantCreateUser,
            CantDoReason::IsNotYourOrder,
            CantDoReason::NotAllowedByStatus,
            CantDoReason::OutOfRangeFiatAmount,
            CantDoReason::OutOfRangeSatsAmount,
            CantDoReason::IsNotYourDispute,
            CantDoReason::NotFound,
            CantDoReason::InvalidFiatCurrency,
            CantDoReason::TooManyRequests,
        ];

        for reason in reasons {
            let cant_do = Message::CantDo(MessageKind::new(
                None,
                None,
                None,
                Action::CantDo,
                Some(Payload::CantDo(Some(reason.clone()))),
            ));
            let message = Message::from_json(&cant_do.as_json().unwrap()).unwrap();
            assert!(message.verify());
            assert_eq!(message.as_json().unwrap(), cant_do.as_json().unwrap());
        }

        // Test None case
        let cant_do = Message::CantDo(MessageKind::new(
            None,
            None,
            None,
            Action::CantDo,
            Some(Payload::CantDo(None)),
        ));
        let message = Message::from_json(&cant_do.as_json().unwrap()).unwrap();
        assert!(message.verify());
        assert_eq!(message.as_json().unwrap(), cant_do.as_json().unwrap());
    }

    // === check_fiat_amount tests ===

    #[test]
    fn test_check_fiat_amount_valid() {
        // id, kind, status, amount, fiat_code, min_amount, max_amount, fiat_amount, payment_method, premium, buyer_pubkey, seller_pubkey, buyer_invoice, created_at, expires_at
        let order = SmallOrder::new(
            None,
            None,
            None,
            100,
            "VES".to_string(),
            None,
            None,
            500,
            "Bank".to_string(),
            1,
            None,
            None,
            None,
            None,
            None,
        );
        assert!(order.check_fiat_amount().is_ok());
    }

    #[test]
    fn test_check_fiat_amount_zero() {
        let order = SmallOrder::new(
            None,
            None,
            None,
            100,
            "VES".to_string(),
            None,
            None,
            0,
            "Bank".to_string(),
            1,
            None,
            None,
            None,
            None,
            None,
        );
        let result = order.check_fiat_amount();
        assert!(result.is_err());
        assert_eq!(result.unwrap_err(), CantDoReason::InvalidAmount);
    }

    #[test]
    fn test_check_fiat_amount_negative() {
        let order = SmallOrder::new(
            None,
            None,
            None,
            100,
            "VES".to_string(),
            None,
            None,
            -100,
            "Bank".to_string(),
            1,
            None,
            None,
            None,
            None,
            None,
        );
        let result = order.check_fiat_amount();
        assert!(result.is_err());
        assert_eq!(result.unwrap_err(), CantDoReason::InvalidAmount);
    }

    // === check_amount tests ===

    #[test]
    fn test_check_amount_valid() {
        // amount = 100000 (positive, valid sats)
        let order = SmallOrder::new(
            None,
            None,
            None,
            100000,
            "VES".to_string(),
            None,
            None,
            500,
            "Bank".to_string(),
            0,
            None,
            None,
            None,
            None,
            None,
        );
        assert!(order.check_amount().is_ok());
    }

    #[test]
    fn test_check_amount_zero() {
        // amount = 0 is valid (seller sets exact sats amount)
        let order = SmallOrder::new(
            None,
            None,
            None,
            0,
            "VES".to_string(),
            None,
            None,
            500,
            "Bank".to_string(),
            0,
            None,
            None,
            None,
            None,
            None,
        );
        assert!(order.check_amount().is_ok());
    }

    #[test]
    fn test_check_amount_negative() {
        // amount = -1000 (negative, invalid)
        let order = SmallOrder::new(
            None,
            None,
            None,
            -1000,
            "VES".to_string(),
            None,
            None,
            500,
            "Bank".to_string(),
            0,
            None,
            None,
            None,
            None,
            None,
        );
        let result = order.check_amount();
        assert!(result.is_err());
        assert_eq!(result.unwrap_err(), CantDoReason::InvalidAmount);
    }
}
