//! Project-owned replacements for external types exposed by C# signatures.
//!
//! These types preserve Rust API identity without copying external member
//! surfaces. Their behavior is implemented only when the owning API slice is
//! ported.

use std::collections::BTreeMap;
use std::future::Future;
use std::marker::PhantomData;
use std::pin::Pin;
use std::sync::{
    Arc,
    atomic::{AtomicBool, Ordering},
};

pub trait Collection<T> {}

pub trait ReadWrite: std::io::Read + std::io::Write {}

impl<T: std::io::Read + std::io::Write> ReadWrite for T {}

#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub enum Object {
    String(String),
}

impl From<String> for Object {
    fn from(value: String) -> Self {
        Self::String(value)
    }
}

impl From<&str> for Object {
    fn from(value: &str) -> Self {
        Self::String(value.to_owned())
    }
}

#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct Utf16CodeUnit(pub u16);

#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct Guid(pub [u8; 16]);

#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub struct Uri(pub String);

#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub struct Regex(pub String);

#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub struct CultureInfo(pub String);

#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub struct ExternalError(pub String);

#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct EnumValue(pub i64);

#[derive(Clone, Debug, Eq, PartialEq)]
pub struct ArrayList(pub Vec<Object>);

#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Array(pub Vec<Object>);

#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Hashtable(pub std::collections::HashMap<Object, Object>);

#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct IDictionaryEnumerator;

#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct IEqualityComparer<T>(pub PhantomData<fn(T)>);

#[derive(Clone, Debug, Eq, PartialEq)]
pub struct StringReader(pub String);

#[derive(Clone, Debug, Eq, PartialEq)]
pub struct StreamReader(pub Vec<u8>);

#[derive(Clone, Debug, Eq, PartialEq)]
pub struct XmlTextReader(pub String);

#[derive(Clone, Debug, Eq, PartialEq)]
pub struct XmlTextWriter(pub String);

#[derive(Clone, Debug, Default)]
pub struct CancellationToken(Arc<AtomicBool>);

impl CancellationToken {
    #[must_use]
    pub fn is_cancellation_requested(&self) -> bool {
        self.0.load(Ordering::Acquire)
    }
}

#[derive(Debug, Default, Eq, Hash, PartialEq)]
pub struct Subscription;

pub type EventHandler<T> = Arc<dyn Fn(T) + Send + Sync>;

pub struct DictionaryKeys<TKey, TValue>(pub Vec<TKey>, pub PhantomData<fn(TValue)>);

pub struct DictionaryEntry(pub Object, pub Object);

pub struct RateLimitLease;

#[derive(Clone, Debug, Default)]
pub struct CancellationTokenSource(CancellationToken);

impl CancellationTokenSource {
    #[must_use]
    pub fn new() -> Self {
        Self::default()
    }

    #[must_use]
    pub fn token(&self) -> CancellationToken {
        self.0.clone()
    }

    pub fn cancel(&self) {
        self.0.0.store(true, Ordering::Release);
    }
}

pub trait Close {
    /// Releases the mapped disposable resource.
    ///
    /// # Errors
    ///
    /// Returns the mapped disposal error, or an unimplemented error until the
    /// owning resource supplies its implementation.
    fn close(&mut self) -> Result<(), ExternalError> {
        Err(ExternalError("Close::close is not implemented".to_owned()))
    }
}

pub struct Thread;

pub struct Task<T>(pub PhantomData<fn() -> T>);

pub struct TaskCompletionSource<T>(pub PhantomData<fn(T)>);

pub struct TimeProvider;

pub struct MediaTypeHeaderValue(pub String);

#[derive(Clone, Debug, Eq, PartialEq)]
pub struct HttpRequest {
    pub method: String,
    pub uri: Uri,
    pub headers: BTreeMap<String, String>,
    pub content_type: Option<String>,
    pub body: Vec<u8>,
}

#[derive(Clone, Debug, Eq, PartialEq)]
pub struct HttpResponse {
    pub status_code: u16,
    pub headers: BTreeMap<String, String>,
    pub content_type: Option<String>,
    pub body: Vec<u8>,
}

impl HttpResponse {
    #[must_use]
    pub const fn is_success_status_code(&self) -> bool {
        self.status_code >= 200 && self.status_code < 300
    }
}

pub type HttpResponseFuture = Pin<Box<dyn Future<Output = HttpResponse> + Send>>;

type HttpHandler = dyn Fn(HttpRequest, CancellationToken) -> HttpResponseFuture + Send + Sync;

#[derive(Clone)]
pub struct HttpMessageHandler(Arc<HttpHandler>);

impl HttpMessageHandler {
    pub fn new<F, Fut>(handler: F) -> Self
    where
        F: Fn(HttpRequest, CancellationToken) -> Fut + Send + Sync + 'static,
        Fut: Future<Output = HttpResponse> + Send + 'static,
    {
        Self(Arc::new(move |request, cancellation_token| {
            Box::pin(handler(request, cancellation_token))
        }))
    }

    #[must_use]
    pub fn send(
        &self,
        request: HttpRequest,
        cancellation_token: CancellationToken,
    ) -> HttpResponseFuture {
        (self.0)(request, cancellation_token)
    }
}

pub struct HttpClient;

pub struct HttpMethod(pub String);

pub struct AuthenticationHeaderValue(pub String);

pub trait IProgress<T>: Send + Sync {}

pub struct IEnumerator;

pub struct WaitHandle;

#[derive(Clone, Debug)]
pub struct FrozenDictionary<TKey, TValue>(pub std::collections::HashMap<TKey, TValue>);

#[derive(Clone, Debug)]
pub struct IDictionary<TKey, TValue>(pub std::collections::HashMap<TKey, TValue>);

#[derive(Clone, Debug)]
pub struct SortedList<TKey, TValue>(pub std::collections::BTreeMap<TKey, TValue>);

#[derive(Clone, Copy, Debug, Default, PartialEq)]
pub struct Matrix4x4(pub [f32; 16]);

#[derive(Clone, Copy, Debug, Default, PartialEq)]
pub struct Quaternion(pub [f32; 4]);

#[derive(Clone, Copy, Debug, Default, PartialEq)]
pub struct Vector3(pub [f32; 3]);

#[derive(Clone, Debug, Eq, PartialEq)]
pub struct StringWriter(pub String);

#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct TypeId(pub &'static str);

pub struct Span<T>(pub PhantomData<fn(T)>);

pub struct RandomSource;

pub struct IAsyncEnumerable<T>(pub PhantomData<fn() -> T>);

pub struct ImmutableDictionary<TKey, TValue>(pub PhantomData<fn(TKey, TValue)>);

pub struct ImmutableList<T>(pub PhantomData<fn(T)>);

pub struct ICollection;

pub struct UnicodeCategory(pub i32);

pub struct TextEncoding;

pub struct SocketException;

#[cfg(test)]
mod tests {
    use super::{CancellationToken, HttpMessageHandler, HttpRequest, HttpResponse, Uri};
    use std::collections::BTreeMap;
    use std::future::Future;
    use std::task::{Context, Poll, Waker};

    #[test]
    fn http_handler_preserves_request_and_response_data() {
        let handler = HttpMessageHandler::new(|request, _cancellation_token| async move {
            HttpResponse {
                status_code: 201,
                headers: request.headers,
                content_type: request.content_type,
                body: request.body,
            }
        });
        let mut response = handler.send(
            HttpRequest {
                method: "PUT".into(),
                uri: Uri("http://example.test/".into()),
                headers: BTreeMap::from([("Location".into(), "slcaps://fixture".into())]),
                content_type: Some("application/llsd+xml".into()),
                body: b"test".to_vec(),
            },
            CancellationToken::default(),
        );
        let mut context = Context::from_waker(Waker::noop());
        let response = match Future::poll(response.as_mut(), &mut context) {
            Poll::Ready(response) => response,
            Poll::Pending => panic!("immediate handler unexpectedly remained pending"),
        };

        assert!(response.is_success_status_code());
        assert_eq!(response.headers["Location"], "slcaps://fixture");
        assert_eq!(
            response.content_type.as_deref(),
            Some("application/llsd+xml")
        );
        assert_eq!(response.body, b"test");
        assert!(
            !HttpResponse {
                status_code: 399,
                headers: BTreeMap::new(),
                content_type: None,
                body: Vec::new(),
            }
            .is_success_status_code()
        );
    }
}
