#![cfg(test)]

//! Test fixtures and mock implementations.
//!
//! This module contains mock transports, signers, and helpers used exclusively
//! in tests. It allows higher-level components to be exercised without opening
//! real HTTP or WebSocket connections.

use std::sync::{Arc, Mutex};
use std::{
    collections::VecDeque,
    pin::Pin,
    task::{Context, Poll},
};

use alloy::primitives::{Address, B256, U256};
use alloy::signers::Signature;
use async_trait::async_trait;
use bytes::Bytes;
use chrono::Utc;
use futures::{Sink, Stream};
use http::StatusCode;
use tokio_tungstenite::tungstenite::{Error, Message};
use uuid::Uuid;

use crate::ClientError;
use crate::signing::signer::PayloadSigner;
use crate::transport::{
    http::{AuthCredentials, HttpResponse, HttpTransport},
    websocket::WsConnector,
};
use crate::types::{Order, OrderStatus, OrderType};
use crate::ws::codec::WsCodec;
use crate::ws::protocol::{UserWsClientMessage, UserWsServerMessage};

/// Mock WebSocket connector for tests.
///
/// Produces in-memory sink/stream pairs backed by shared state, without opening
/// a network connection.
#[derive(Clone, Default)]
pub struct MockWsConnector {
    /// Shared state backing the produced sink/stream pair.
    inner: Arc<Mutex<MockWsState>>,
}

/// Internal shared state used by the mock WebSocket sink and stream.
#[derive(Debug, Default)]
struct MockWsState {
    /// Messages sent by the client through the mock sink.
    outgoing: Vec<Message>,
    /// Messages yielded to the client by the mock stream (FIFO order).
    incoming: VecDeque<Result<Message, Error>>,
}

/// Constructors for configuring mock WebSocket connector behavior.
impl MockWsConnector {
    /// Create a connector that yields no incoming messages.
    pub fn with_empty_stream() -> Self {
        Self::default()
    }

    /// Create a connector that yields the provided incoming messages in order.
    pub fn with_incoming(messages: Vec<Message>) -> Self {
        let mut state = MockWsState::default();
        state.incoming = messages.into_iter().map(Ok).collect();

        Self {
            inner: Arc::new(Mutex::new(state)),
        }
    }
}

/// Mock WebSocket sink.
///
/// Records messages sent by the client into shared state.
#[derive(Debug)]
pub struct MockSink {
    /// Shared state where outgoing messages are recorded.
    state: Arc<Mutex<MockWsState>>,
}

/// Sink implementation that appends outgoing messages to the shared state.
impl Sink<Message> for MockSink {
    type Error = Error;

    fn poll_ready(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
        Poll::Ready(Ok(()))
    }

    fn start_send(self: Pin<&mut Self>, item: Message) -> Result<(), Self::Error> {
        self.state.lock().unwrap().outgoing.push(item);
        Ok(())
    }

    fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
        Poll::Ready(Ok(()))
    }

    fn poll_close(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
        Poll::Ready(Ok(()))
    }
}

/// Mock WebSocket stream.
///
/// Yields scripted incoming messages from shared state.
#[derive(Debug)]
pub struct MockStream {
    /// Shared state from which incoming messages are consumed.
    state: Arc<Mutex<MockWsState>>,
}

/// Stream implementation that pops incoming messages from the shared queue.
impl Stream for MockStream {
    type Item = Result<Message, Error>;

    fn poll_next(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
        let mut state = self.state.lock().unwrap();
        Poll::Ready(state.incoming.pop_front())
    }
}

/// `WsConnector` implementation that returns mock sink/stream handles.
#[async_trait]
impl WsConnector for MockWsConnector {
    /// Sink handle used to send WebSocket messages.
    type Sink = MockSink;
    /// Stream handle used to receive WebSocket messages.
    type Stream = MockStream;

    /// Create a new mock sink/stream pair backed by the connector's shared state.
    async fn connect(&self) -> Result<(Self::Sink, Self::Stream), Error> {
        Ok((
            MockSink {
                state: self.inner.clone(),
            },
            MockStream {
                state: self.inner.clone(),
            },
        ))
    }
}

/// Mock HTTP transport used in tests.
///
/// Returns predefined responses in FIFO order for each request.
#[derive(Clone, Default)]
pub struct MockHttp {
    /// Queue of responses returned in order of calls.
    q: Arc<Mutex<VecDeque<HttpResponse>>>,
}

/// Constructors for configuring mock HTTP responses.
impl MockHttp {
    /// Push a predefined HTTP response onto the mock response queue.
    ///
    /// Responses are returned in FIFO order for subsequent HTTP requests.
    pub fn push(&self, status: StatusCode, body: impl Into<Bytes>) {
        self.q.lock().unwrap().push_back(HttpResponse {
            status,
            body: body.into(),
        });
    }
}

/// `HttpTransport` implementation that pops responses from the predefined queue.
#[async_trait]
impl HttpTransport for MockHttp {
    async fn get(&self, _path: &str) -> Result<HttpResponse, ClientError> {
        Ok(self
            .q
            .lock()
            .unwrap()
            .pop_front()
            .expect("no mock response"))
    }

    async fn get_authenticated(
        &self,
        _path: &str,
        _auth: &AuthCredentials,
    ) -> Result<HttpResponse, ClientError> {
        Ok(self
            .q
            .lock()
            .unwrap()
            .pop_front()
            .expect("no mock response"))
    }

    async fn post_json<T: serde::Serialize + Send + Sync>(
        &self,
        _path: &str,
        _body: &T,
    ) -> Result<HttpResponse, ClientError> {
        Ok(self
            .q
            .lock()
            .unwrap()
            .pop_front()
            .expect("no mock response"))
    }

    async fn delete(
        &self,
        _path: &str,
        _auth: &AuthCredentials,
    ) -> Result<HttpResponse, ClientError> {
        Ok(self
            .q
            .lock()
            .unwrap()
            .pop_front()
            .expect("no mock response"))
    }
}

/// Test signer that always returns the fixed signature `"0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b"`.
pub struct StaticSigner;

/// `PayloadSigner` implementation that always succeeds with a constant value.
#[async_trait]
impl PayloadSigner for StaticSigner {
    async fn sign_hash(&self, _digest: &B256) -> Result<Signature, ClientError> {
        Ok(Signature::from_bytes_and_parity(&[0u8; 64], false))
    }
}

/// Deterministic challenge signer for tests.
///
/// Always returns the same signature string regardless of input.
pub struct FixedSigner {
    /// Signature string returned by the signer.
    pub sig: &'static str,
}

/// Test implementation of `PayloadSigner` that always succeeds.
#[async_trait]
impl PayloadSigner for FixedSigner {
    async fn sign_hash(&self, _digest: &B256) -> Result<Signature, ClientError> {
        Ok(Signature::from_bytes_and_parity(&[0u8; 64], false))
    }
}

/// Challenge signer that always fails.
///
/// Used to validate authentication error handling paths.
pub struct FailingSigner;

/// Test implementation of `PayloadSigner` that always returns an error.
#[async_trait]
impl PayloadSigner for FailingSigner {
    async fn sign_hash(&self, _digest: &B256) -> Result<Signature, ClientError> {
        Err(ClientError::Auth("sign failed".into()))
    }
}

/// Construct a deterministic `serde_json::Error` for tests.
fn make_serde_json_error() -> serde_json::Error {
    serde_json::from_str::<serde_json::Value>("{not valid json").unwrap_err()
}

/// Test codec that always fails encoding/decoding.
///
/// Used to exercise error propagation from the WebSocket codec layer.
#[derive(Clone)]
pub struct FailingCodec;

/// `WsCodec` implementation that returns a deterministic failure.
impl WsCodec for FailingCodec {
    fn encode_client(&self, _msg: &UserWsClientMessage) -> Result<String, ClientError> {
        Err(ClientError::Serde(make_serde_json_error()))
    }

    fn decode_server(&self, _text: &str) -> Result<UserWsServerMessage, ClientError> {
        unreachable!("not needed for this test");
    }
}

/// Sink that records outgoing WebSocket messages into an in-memory vector.
pub struct VecSink {
    /// Messages sent through the sink.
    sent: Arc<Mutex<Vec<Message>>>,
}

impl VecSink {
    /// Construct a new mock WebSocket sink.
    ///
    /// Returns the sink along with shared storage containing all messages
    /// sent through the sink.
    pub fn new() -> (Self, Arc<Mutex<Vec<Message>>>) {
        let shared = Arc::new(Mutex::new(Vec::new()));
        (
            Self {
                sent: shared.clone(),
            },
            shared,
        )
    }
}

/// `Sink` implementation that appends messages to the internal vector.
impl Sink<Message> for VecSink {
    type Error = tokio_tungstenite::tungstenite::Error;

    fn poll_ready(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
        Poll::Ready(Ok(()))
    }

    fn start_send(self: Pin<&mut Self>, item: Message) -> Result<(), Self::Error> {
        self.sent.lock().unwrap().push(item);
        Ok(())
    }

    fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
        Poll::Ready(Ok(()))
    }

    fn poll_close(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
        Poll::Ready(Ok(()))
    }
}

/// Stream that yields a predefined sequence of WebSocket messages.
pub struct VecStream {
    /// Predefined messages yielded by the stream (FIFO order).
    q: VecDeque<Result<Message, Error>>,
}

impl VecStream {
    /// Construct a stream yielding the provided WebSocket messages in order.
    pub fn new(items: Vec<Result<Message, Error>>) -> Self {
        Self { q: items.into() }
    }
}

/// `Stream` implementation that pops messages from the internal queue.
impl Stream for VecStream {
    type Item = Result<Message, Error>;

    fn poll_next(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
        Poll::Ready(self.q.pop_front())
    }
}

/// Construct a deterministic test address derived from `n`.
pub fn addr(n: u8) -> Address {
    let mut bytes = [0u8; 20];
    bytes[19] = n;
    Address::from(bytes)
}

/// Returns a fully valid Order suitable for REST and WS tests.
///
/// This is the *canonical* test fixture for the SDK.
/// If the Order struct evolves, update it here once.
pub fn valid_order() -> Order {
    Order {
        id: Uuid::nil(),
        order_type: OrderType::Mint,
        order_status: OrderStatus::Pending,
        nonce: U256::from(1u64),
        expiry: (Utc::now().timestamp() + 3600) as u64,
        payer: addr(1),
        recipient: addr(2),
        collateral_token: addr(3),
        collateral_amount: U256::from(1_000u64),
        asset_amount: U256::from(100u64),
        signature: Some(dummy_signature()),
        controller: addr(4),
        created: Utc::now().timestamp() as u64,
        tx_hash: None,
    }
}

fn dummy_signature() -> Signature {
    Signature::from_bytes_and_parity(&[0u8; 64], false)
}
