use crate::client::AccessTokenCache;
use crate::handlers::{
    CallbackHandler, DefaultLifecycleListener, EventHandler, LifecycleListener, SystemHandler,
};
use crate::{ClientConfig, Credential};
use std::collections::HashMap;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use tokio::sync::RwLock;

mod access_token;
mod download_resources;
mod upload_resources;
pub use upload_resources::*;
mod handle_message;
mod lifecycle;
mod send_message;
use crate::frames::down_message::MessageTopic;
pub use download_resources::DingtalkResource;

/// DingTalk Stream Client
pub struct DingTalkStream {
    /// Credential for authentication
    pub(super) credential: Credential,
    /// Client configuration
    config: ClientConfig,
    /// Event handler
    event_handler: Option<Arc<dyn EventHandler>>,
    /// Callback handlers mapped by topic
    callback_handlers: HashMap<MessageTopic, Arc<dyn CallbackHandler>>,
    /// System handler
    system_handler: Option<Arc<dyn SystemHandler>>,
    /// Whether connected
    connected: AtomicBool,
    /// Whether registered
    registered: AtomicBool,
    /// Access token cache
    access_token: Arc<RwLock<Option<AccessTokenCache>>>,
    http_client: reqwest::Client,
    lifecycle_listener: Arc<dyn LifecycleListener>,
}

impl DingTalkStream {
    /// Create a new DingTalk Stream client
    pub fn new(credential: Credential) -> Self {
        Self::with_config(credential, ClientConfig::default())
    }

    /// Create with custom configuration
    pub fn with_config(credential: Credential, config: ClientConfig) -> Self {
        Self {
            credential,
            config,
            event_handler: None,
            callback_handlers: HashMap::default(),
            system_handler: None,
            connected: AtomicBool::new(false),
            registered: AtomicBool::new(false),
            access_token: Default::default(),
            http_client: reqwest::Client::default(),
            lifecycle_listener: Arc::new(DefaultLifecycleListener::default()),
        }
    }
}

impl DingTalkStream {
    /// Register an event handler
    pub async fn register_event_handler<H: EventHandler + 'static>(
        mut self,
        handler: Arc<H>,
    ) -> Self {
        self.event_handler.replace(handler);
        self
    }

    /// Register a callback handler for a specific topic
    pub async fn register_callback_handler<H: CallbackHandler + 'static>(
        mut self,
        handler: Arc<H>,
    ) -> Self {
        let topic = handler.topic().clone();
        self.callback_handlers.insert(topic, handler);
        self
    }

    /// Register a system handler
    pub async fn register_system_handler<H: SystemHandler + 'static>(
        mut self,
        handler: Arc<H>,
    ) -> Self {
        self.system_handler.replace(handler);
        self
    }

    pub async fn register_lifecycle_listener<L: LifecycleListener + 'static>(
        mut self,
        listener: Arc<L>,
    ) -> Self {
        self.lifecycle_listener = listener;
        self
    }
}

impl DingTalkStream {
    /// Check if connected
    pub fn is_connected(&self) -> bool {
        self.connected.load(Ordering::Relaxed)
    }

    /// Check if registered
    pub fn is_registered(&self) -> bool {
        self.registered.load(Ordering::Relaxed)
    }

    /// Get the credential
    pub fn credential(&self) -> &Credential {
        &self.credential
    }

    /// Get configuration
    pub fn config(&self) -> &ClientConfig {
        &self.config
    }
}
