pub mod faker;
pub mod game;
pub mod packet;
pub mod player;
pub mod protocol;
pub mod server;

use std::{
  collections::HashMap,
  io::Cursor,
  net::{SocketAddr, ToSocketAddrs},
  sync::{Arc, LazyLock},
};

use faker::Faker;
use packet::{rw::read_packet, Packet};
use player::PlayerActor;
use server::{web_main, Manager};
use tokio::{
  net::{TcpListener, UdpSocket},
  sync::RwLock,
};
use tracing::{error, info, Level};
use tracing_subscriber::{filter::FilterFn, layer::SubscriberExt, util::SubscriberInitExt, Layer};
use xtra::{prelude::MessageChannel, Address, Mailbox};

type PacketChannel = MessageChannel<Packet, (), xtra::refcount::Strong>;
pub fn clients() -> &'static RwLock<HashMap<u128, (PacketChannel, SocketAddr)>> {
  static CLIENTS: LazyLock<RwLock<HashMap<u128, (PacketChannel, SocketAddr)>>> = LazyLock::new(|| RwLock::default());

  &CLIENTS
}


pub fn manager() -> &'static Address<Manager> {
  static MANAGER: LazyLock<Address<Manager>> = LazyLock::new(web_main);

  &MANAGER
}


#[tokio::main]
async fn main() {
  tracing_subscriber::registry()
    .with(tracing_subscriber::fmt::layer().with_filter(FilterFn::new(|meta| {
      *meta.level() < Level::INFO || meta.module_path().unwrap().contains("smo_server")
    })))
    .init();
  let tcp = TcpListener::bind("0.0.0.0:1027").await.unwrap();

  {
    let (address, mailbox) = Mailbox::unbounded();
    let address = xtra::spawn_tokio(
      Faker {
        address: address.clone(),
      },
      (address, mailbox),
    );
    clients().write().await.insert(
      1,
      (
        MessageChannel::new(address),
        "0.0.0.0:1027".to_socket_addrs().unwrap().next().unwrap(),
      ),
    );
  }

  let socket = Arc::new(UdpSocket::bind("0.0.0.0:1027").await.unwrap());
  tokio::spawn({
    let socket = socket.clone();
    async move {
      loop {
        let mut packet = [0u8; 256];
        let (size, _addr) = socket.recv_from(&mut packet).await.unwrap();
        // trace!("got packet from {_addr:?}");
        match read_packet(&mut Cursor::new(&mut packet[..size]), true).await {
          Ok(packet) => {
            // info!("packet from udp {packet:?}");
            if let Some(client) = clients().read().await.get(&packet.user_id) {
              let _ = client.0.send(packet).detach().await;
            }
          }
          Err(error) => {
            error!("udp packet error: {error:?}");
          }
        };
      }
    }
  });

  info!("listening on port 1027");
  loop {
    match tcp.accept().await {
      Ok((stream, addr)) => {
        PlayerActor::new_connection(stream, addr, socket.clone());
      }
      Err(error) => {
        error!("failed to handle connection: {error:?}");
      }
    }
  }
}
