use std::{collections::HashMap, sync::Arc};

use avam_protocol::Packets;
use axum::extract::FromRef;
use leptos::get_configuration;
use tokio::sync::{mpsc::Sender, RwLock};

use crate::{
    config::Config,
    domain::api::{ports::ApiService, prelude::User},
};

#[derive(Clone)]
/// The global application state shared between all request handlers.
pub struct AppState<S>
where
    S: ApiService,
{
    pub leptos_options: leptos::LeptosOptions,
    config: Arc<Config>,
    api_service: Arc<S>,
    connections: Arc<RwLock<HashMap<uuid::Uuid, Sender<Packets>>>>,
}

impl<S> AppState<S>
where
    S: ApiService,
{
    pub async fn new(api_service: S, config: Config) -> Self {
        Self {
            config: Arc::new(config),
            leptos_options: get_configuration(None).await.unwrap().leptos_options,
            api_service: Arc::new(api_service),
            connections: Arc::new(RwLock::new(HashMap::new())),
        }
    }

    pub fn config(&self) -> Arc<Config> {
        self.config.clone()
    }

    pub fn api_service(&self) -> Arc<S> {
        self.api_service.clone()
    }
}

impl<S> AppState<S>
where
    S: ApiService,
{
    pub async fn add_connection(&self, user: &User, sender: Sender<Packets>) {
        self.connections.write().await.insert(user.id(), sender);
    }

    pub async fn has_connection(&self, user: &User) -> bool {
        self.connections.read().await.contains_key(&user.id())
    }

    pub async fn remove_connection(&self, user: &User) {
        self.connections.write().await.remove(&user.id());
    }

    pub async fn send(&self, user: &User, packet: Packets) -> Result<(), anyhow::Error> {
        if let Some(sender) = self.connections.read().await.get(&user.id()) {
            sender.send(packet).await?;
        }
        Ok(())
    }
}

impl<S> FromRef<AppState<S>> for leptos::LeptosOptions
where
    S: ApiService,
{
    fn from_ref(input: &AppState<S>) -> Self {
        input.leptos_options.clone()
    }
}
