use crate::ssh::ReceiveSyncService;
use crate::ssh::SshTokenService;
use crate::ssh::handle::SSHandle;
use db::cache::AppCache;
use db::database::AppDatabase;
use deadpool_redis::cluster::Pool as RedisPool;
use russh::server::Handler;
use std::io;
use std::net::SocketAddr;

pub struct SSHServer {
    pub db: AppDatabase,
    pub cache: AppCache,
    pub redis_pool: RedisPool,
    pub token_service: SshTokenService,
}

impl SSHServer {
    pub fn new(
        db: AppDatabase,
        cache: AppCache,
        redis_pool: RedisPool,
        token_service: SshTokenService,
    ) -> Self {
        SSHServer {
            db,
            cache,
            redis_pool,
            token_service,
        }
    }
}
impl russh::server::Server for SSHServer {
    type Handler = SSHandle;

    fn new_client(&mut self, addr: Option<SocketAddr>) -> Self::Handler {
        if let Some(addr) = addr {
            tracing::info!("New SSH connection ip={} port={}", addr.ip(), addr.port());
        } else {
            tracing::info!("New SSH connection from unknown address");
        }
        let sync_service = ReceiveSyncService::new(self.redis_pool.clone());
        SSHandle::new(
            self.db.clone(),
            self.cache.clone(),
            sync_service,
            self.token_service.clone(),
            addr,
        )
    }

    fn handle_session_error(&mut self, error: <Self::Handler as Handler>::Error) {
        match error {
            russh::Error::Disconnect => {
                tracing::info!("Connection disconnected by peer");
            }
            russh::Error::Inconsistent => {
                tracing::warn!("Protocol inconsistency detected");
            }
            russh::Error::NotAuthenticated => {
                tracing::warn!("Authentication failed");
            }
            russh::Error::IO(ref io_err) => {
                tracing::warn!(
                    "SSH IO error kind={:?} message={} raw_os_error={:?}",
                    io_err.kind(),
                    io_err,
                    io_err.raw_os_error()
                );

                if io_err.kind() == io::ErrorKind::UnexpectedEof {
                    tracing::warn!("Client disconnected during handshake or before authentication");
                }
            }
            _ => {
                tracing::warn!("SSH session error error={}", error);
            }
        }
    }
}
