// Copyright Marcus Del Favero 2025
// Licensed under the GNU AGPLv3 with an exception, see `README.md` for details
#[cfg(feature = "client")] use std::time::Duration;

use std::{mem, net::SocketAddr};

use tokio::sync::oneshot::{self, Sender as OneshotSender};

use super::{sender::{SenderTask, SerpResponse}, receiver::{ReceiverTask, ReceiverTaskBuilder}, error::ErrorContainer, super::{MessageStream, DynMs, MsError}};
#[cfg(feature = "client")] use super::super::Security;

use crate::protocol::message::{Message, WorldMessage, MiscMessage, text_io::ClientTextInput};
use crate::net::{serp::{Error as SerpError, ConnectionHandle}, server::{SHUTTING_DOWN_MSG, SHUTTING_DOWN_STATUS}, serp::PendingResponse};
use crate::server::multiplayer::connection_listener::IncomingConnection;
use crate::world::player::update::{PlayerUpdate, PlayerBulkUpdate};

pub struct ServerMessageStream {
	world_messages: Vec<WorldMessage>,
	misc_messages: Vec<MiscMessage>,
	world_sender: SenderTask<Vec<WorldMessage>>,
	misc_sender: SenderTask<Vec<MiscMessage>>,
	receiver: ReceiverTask,
	conn: ConnectionHandle,
	error: ErrorContainer,
	addr: SocketAddr,
}

pub struct IncomingServerMessageStream {
	stream: ServerMessageStream,
	response_sender: OneshotSender<SerpResponse>,
}

impl IncomingServerMessageStream {
	pub async fn try_new(pr: PendingResponse, addr: SocketAddr) -> Result<IncomingServerMessageStream, String> {
		let (conn, mut world_sender_stream, world_receiver_stream) = pr.into_pre_connection();

		world_sender_stream.set_priority(1)?;
		let misc_stream = conn.open_stream().await?;

		let error = ErrorContainer::default();
		let (response_sender, response_receiver) = oneshot::channel();

		let world_sender = SenderTask::<Vec<WorldMessage>>::new_pending_response(world_sender_stream, response_receiver, error.clone());
		let receiver = ReceiverTaskBuilder::new(error.clone()).add_stream::<(PlayerUpdate, u64)>(world_receiver_stream, 1 << 8).add_stream::<ClientTextInput>(misc_stream.receiver, 1 << 16);
		let datagram_sender = receiver.get_sender();
		let receiver = receiver.build();

		let misc_sender = SenderTask::<Vec<MiscMessage>>::new(misc_stream.sender, error.clone());

		{
			let error = error.clone();
			let conn = conn.clone();
			tokio::spawn(async move {
				while let Ok(data) = conn.recv_datagram().await {
					match PlayerBulkUpdate::deserialise(&data) {
						Ok(bulk_update) => _ = datagram_sender.send(Message::PlayerBulkUpdate(bulk_update)).await,
						Err(err) => {
							error.insert(SerpError::Deserialisation(format!("failed deserialising bulk update: {err}")));
							return;
						},
					}
				}
			});
		}

		let stream = ServerMessageStream { world_messages: Vec::new(), misc_messages: Vec::new(), world_sender, misc_sender, receiver, conn, error, addr };
		Ok(IncomingServerMessageStream { stream, response_sender })
	}
}

impl IncomingConnection for IncomingServerMessageStream {
	fn accept(self: Box<Self>) -> Box<DynMs> {
		let _ = self.response_sender.send(SerpResponse::Success);
		Box::new(self.stream)
	}

	fn reject(self: Box<Self>, reason: String) {
		let _ = self.response_sender.send(SerpResponse::Understood(reason));
	}

	fn source(&self) -> String {
		self.stream.addr.to_string()
	}
}

impl MessageStream for ServerMessageStream {
	fn send(&mut self, msg: Message) -> Result<(), MsError> {
		match msg {
			Message::World(msg) => self.world_messages.push(msg),
			Message::Misc(msg) => self.misc_messages.push(msg),
			_ => unimplemented!(),
		}

		Ok(())
	}

	fn receive(&mut self) -> Result<Option<Message>, MsError> {
		Ok(self.receiver.receive(&self.error)?)
	}

	fn flush(&mut self) -> Result<(), MsError> {
		self.world_sender.send(mem::take(&mut self.world_messages), &self.error)?;
		Ok(self.misc_sender.send(mem::take(&mut self.misc_messages), &self.error)?)
	}

	#[cfg(feature = "client")]
	fn is_networked(&self) -> bool {
		true
	}

	fn close(self: Box<Self>) {
		self.conn.close(SHUTTING_DOWN_STATUS, SHUTTING_DOWN_MSG);
	}

	fn source(&self) -> String {
		self.addr.to_string()
	}

	#[cfg(feature = "client")]
	fn ping(&self) -> Duration {
		self.conn.rtt()
	}

	#[cfg(feature = "client")]
	fn security(&self) -> Security {
		log::warn!("unimplemented");
		Security::Insecure
	}
}

impl From<(PlayerUpdate, u64)> for Message {
	fn from(msg: (PlayerUpdate, u64)) -> Message {
		Message::PlayerUpdate(msg.0, msg.1)
	}
}

impl From<ClientTextInput> for Message {
	fn from(msg: ClientTextInput) -> Message {
		Message::ClientTextInput(msg)
	}
}
