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 slog::{Logger, info, warn};
use std::io;
use std::net::SocketAddr;

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

impl SSHServer {
    pub fn new(
        db: AppDatabase,
        cache: AppCache,
        redis_pool: RedisPool,
        logger: Logger,
        token_service: SshTokenService,
    ) -> Self {
        SSHServer {
            db,
            cache,
            redis_pool,
            logger,
            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 {
            info!(
                self.logger,
                "New SSH connection from {}:{}",
                addr.ip(),
                addr.port()
            );
        } else {
            info!(self.logger, "New SSH connection from unknown address");
        }
        let sync_service = ReceiveSyncService::new(self.redis_pool.clone(), self.logger.clone());
        SSHandle::new(
            self.db.clone(),
            self.cache.clone(),
            sync_service,
            self.logger.clone(),
            self.token_service.clone(),
            addr,
        )
    }

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

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