//! Main server implementation

use crate::WorkerClass;
use crate::config::ServerConfig;
use crate::control::ipc as control_ipc;
use crate::errors::{FalcornError, Result};
use crate::logging::{Logger, ipc, start_logger_client};
use crate::runtime::{
    async_worker, sync_worker,
    worker_manager::{WorkerManager, WorkerSnapshotState},
};

use std::os::unix::net::{UnixListener, UnixStream};
use std::sync::atomic::AtomicBool;
use std::sync::mpsc;
use std::sync::{Arc, Mutex};
use std::time::{Duration, Instant};

/// Serveer Execution mode
#[derive(Debug, Clone, PartialEq)]
pub enum ServerMode {
    Master, // Master process with workers
    Worker, // Worker Process (handles requests)
}

/// Main Falcorn server
#[derive(Clone)]
pub struct FalcornServer {
    config: Arc<Mutex<ServerConfig>>,
    logger: Arc<Logger>,
    mode: ServerMode,
    worker_id: Option<u32>,
    shutdown: Arc<AtomicBool>,
    start_time: Instant,
}

impl FalcornServer {
    /// Create a new instance of FalcornSerrver (Master)
    pub fn new(config: ServerConfig) -> Result<Self> {
        // Validate confiig
        config.validate()?;

        // Create logger
        let config_snapshot = config.clone();
        let socket = ipc::socket_path();
        let tx = start_logger_client(&socket);
        let logger = Arc::new(
            Logger::new(
                config_snapshot.log_level_enum(),
                tx,
                config_snapshot.log_stdout,
            )
            .with_access_enabled(config_snapshot.access_events_enabled()),
        );

        logger.info(|| {
            format!(
                "Initializing Falcorn Master Server on {} (PID: {})",
                config.bind_addr(),
                std::process::id()
            )
        });

        Ok(Self {
            config: Arc::new(Mutex::new(config)),
            logger,
            mode: ServerMode::Master,
            worker_id: None,
            shutdown: Arc::new(AtomicBool::new(false)),
            start_time: Instant::now(),
        })
    }

    /// Create a worker instance (for child prorcess)
    pub fn new_worker(config: ServerConfig, worker_id: u32) -> Result<Self> {
        let config_snapshot = config.clone();
        let socket = ipc::socket_path();
        let tx = start_logger_client(&socket);
        let logger = Arc::new(
            Logger::new(
                config_snapshot.log_level_enum(),
                tx,
                config_snapshot.log_stdout,
            )
            .with_access_enabled(config_snapshot.access_events_enabled())
            .with_worker_id(worker_id),
        );

        Ok(Self {
            config: Arc::new(Mutex::new(config)),
            logger,
            mode: ServerMode::Worker,
            worker_id: Some(worker_id),
            shutdown: Arc::new(AtomicBool::new(false)),
            start_time: Instant::now(),
        })
    }

    /// Run server based on mode
    pub fn run(&mut self) -> Result<()> {
        match self.mode {
            ServerMode::Master => self.run_master(), //.await,
            ServerMode::Worker => self.run_worker(), //.await,
        }
    }

    /// Run Server in Master mode (Manage workers)
    fn run_master(&mut self) -> Result<()> {
        self.logger.info(|| {
            let config = self.config.lock().expect("config mutex poisoned");
            format!(
                "[Master] Falcorn {} starting with {} workers",
                env!("CARGO_PKG_VERSION"),
                config.worker_count()
            )
        });

        // Create a Worker Manager
        let mut manager = WorkerManager::new(
            Arc::clone(&self.config),
            Arc::clone(&self.logger),
            self.shutdown.clone(),
        );

        let snapshots = manager.snapshots();
        let commands = manager.commands();
        self.start_control_server(snapshots, commands);

        // Setup master master signals
        self.setup_master_signals(&mut manager)?;

        // Spawn all workers
        manager.spawn_workers()?;

        self.logger.info(|| {
            let config = self.config.lock().expect("config mutex poisoned");
            format!(
                "[Master] ✓ All workers spawned successfully ({})",
                config.worker_class.as_str()
            )
        });

        // Monitor workers
        if let Err(e) = manager.monitor_workers() {
            self.logger
                .error(|| format!("[Master] Monitor error: {}", e));
        }

        // Graceful shutdown
        self.logger
            .info(|| "[Master] Initiating graceful shutdown".to_string());
        manager.shutdown_workers(true)?;

        self.logger
            .info(|| "[Master] ✓ Falcorn Server stopped gracefully".to_string());

        Ok(())
    }

    /// Run server in Worker mode (handles requests)
    fn run_worker(&mut self) -> Result<()> {
        let worker_id = self
            .worker_id
            .ok_or_else(|| FalcornError::runtime("Worker ID not set"))?;

        let config = self.config.lock().expect("config mutex poisoned").clone();
        match config.worker_class {
            WorkerClass::Sync => sync_worker::run_worker_process(worker_id, config),
            WorkerClass::Async => {
                async_worker::run_async_worker_with_runtime(worker_id, config) //.await
            }
        }
    }

    fn start_control_server(
        &self,
        snapshots: Arc<Mutex<WorkerSnapshotState>>,
        commands: Arc<Mutex<std::collections::VecDeque<control_ipc::ControlCommand>>>,
    ) {
        let Some(path) = self
            .config
            .lock()
            .expect("config mutex poisoned")
            .control_socket
            .clone()
        else {
            return;
        };

        let logger = Arc::clone(&self.logger);
        let config = Arc::clone(&self.config);
        let shutdown = self.shutdown.clone();
        let start_time = self.start_time;

        std::thread::spawn(move || {
            if let Err(e) = run_control_server(
                &path,
                config,
                logger.clone(),
                shutdown,
                snapshots,
                commands,
                start_time,
            ) {
                logger.error(|| format!("[Control] Control IPC failed: {}", e));
            }
        });
    }

    /// Setup signals for master process
    fn setup_master_signals(&self, _manager: &mut WorkerManager) -> Result<()> {
        use signal_hook::consts::signal::*;
        use signal_hook::iterator::Signals;

        let logger = Arc::clone(&self.logger);

        let shutdown = self.shutdown.clone();

        std::thread::spawn(move || {
            let mut signals = match Signals::new(&[SIGTERM, SIGINT, SIGHUP, SIGUSR1]) {
                Ok(s) => s,
                Err(e) => {
                    logger.error(|| format!("[Master] Failed to create signal handler: {}", e));
                    shutdown.store(true, std::sync::atomic::Ordering::Relaxed);
                    return;
                }
            };

            for sig in signals.forever() {
                match sig {
                    SIGTERM | SIGINT => {
                        logger.info(|| "[Master] Received shutdown signal".to_string());
                        // manager will auto detect the shutdown via its flag
                        // manager.shutdown_workers(true);
                        shutdown.store(true, std::sync::atomic::Ordering::Relaxed);

                        break;
                    }
                    SIGHUP => {
                        logger
                            .info(|| "[Master] Received SIGHUP (reload configuration)".to_string());
                        // TODO: Implement config reload
                    }
                    SIGUSR1 => {
                        logger.info(|| "[Master] Received SIGUSR1 (reopen logs)".to_string());
                        // TODO: reopen logs
                    }
                    _ => {}
                }
            }
        });

        Ok(())
    }

    /// Get Server status
    pub fn status(&self) -> String {
        match self.mode {
            ServerMode::Master => {
                let config = self.config.lock().expect("config mutex poisoned");
                format!(
                    "Falcorn Master Server\n  Address: {}\n  Workers: {} ({})\n  App: {}:{}\n  PID: {}",
                    config.bind_addr(),
                    config.worker_count(),
                    config.worker_class.as_str(),
                    config.app_module,
                    config.app_callable,
                    std::process::id()
                )
            }
            ServerMode::Worker => {
                let config = self.config.lock().expect("config mutex poisoned");
                format!(
                    "Falcorn Worker\n  ID: {}\n  PID: {}\n  Mode: {}",
                    self.worker_id.unwrap_or(0),
                    std::process::id(),
                    config.worker_class.as_str()
                )
            }
        }
    }
}

fn run_control_server(
    path: &str,
    config: Arc<Mutex<ServerConfig>>,
    logger: Arc<Logger>,
    shutdown: Arc<AtomicBool>,
    snapshots: Arc<Mutex<WorkerSnapshotState>>,
    commands: Arc<Mutex<std::collections::VecDeque<control_ipc::ControlCommand>>>,
    start_time: Instant,
) -> Result<()> {
    const SUBSCRIBE_TIMEOUT: Duration = Duration::from_secs(5);
    const WRITE_TIMEOUT: Duration = Duration::from_millis(500);

    let _ = std::fs::remove_file(path);
    let listener = UnixListener::bind(path)
        .map_err(|e| FalcornError::runtime(format!("Control IPC bind error: {}", e)))?;
    {
        let config_guard = config.lock().expect("config mutex poisoned");
        control_ipc::set_control_socket_permissions(path, config_guard.control_socket_mode)?;
    }

    let (acl_mode, auth_token) = {
        let config_guard = config.lock().expect("config mutex poisoned");
        (
            control_ipc::parse_control_acl_mode(&config_guard.control_acl_mode)?,
            config_guard.control_auth_token.clone(),
        )
    };

    listener
        .set_nonblocking(true)
        .map_err(FalcornError::IoError)?;

    logger.info(|| format!("[Control] Listening on {}", path));

    while !shutdown.load(std::sync::atomic::Ordering::Relaxed) {
        match listener.accept() {
            Ok((stream, _)) => {
                let config = Arc::clone(&config);
                let logger = Arc::clone(&logger);
                let snapshots = Arc::clone(&snapshots);
                let commands = Arc::clone(&commands);
                let auth_token = auth_token.clone();
                std::thread::spawn(move || {
                    if let Err(e) = handle_control_client(
                        stream,
                        &config,
                        auth_token.as_deref(),
                        acl_mode,
                        snapshots,
                        commands,
                        start_time,
                        SUBSCRIBE_TIMEOUT,
                        WRITE_TIMEOUT,
                    ) {
                        logger.warn(|| format!("[Control] Client closed: {}", e));
                    }
                });
            }
            Err(e) if e.kind() == std::io::ErrorKind::WouldBlock => {
                std::thread::sleep(Duration::from_millis(200));
            }
            Err(e) => {
                return Err(FalcornError::runtime(format!(
                    "Control IPC accept error: {}",
                    e
                )));
            }
        }
    }

    Ok(())
}

fn handle_control_client(
    mut stream: UnixStream,
    config: &Arc<Mutex<ServerConfig>>,
    auth_token: Option<&str>,
    acl_mode: control_ipc::ControlAclMode,
    snapshots: Arc<Mutex<WorkerSnapshotState>>,
    commands: Arc<Mutex<std::collections::VecDeque<control_ipc::ControlCommand>>>,
    start_time: Instant,
    subscribe_timeout: Duration,
    write_timeout: Duration,
) -> Result<()> {
    stream
        .set_nonblocking(false)
        .map_err(FalcornError::IoError)?;
    stream
        .set_read_timeout(Some(subscribe_timeout))
        .map_err(FalcornError::IoError)?;
    stream
        .set_write_timeout(Some(write_timeout))
        .map_err(FalcornError::IoError)?;

    control_ipc::enforce_peer_acl(&stream, acl_mode)?;

    let (frame_type, payload) = control_ipc::read_frame(&mut stream)?;
    if frame_type != control_ipc::ControlFrameType::Subscribe {
        let err = control_ipc::ControlErrorFrame {
            code: "invalid_subscription".to_string(),
            message: "Expected subscribe frame as first control message".to_string(),
        };
        let _ =
            control_ipc::write_typed_frame(&mut stream, control_ipc::ControlFrameType::Error, &err);
        return Err(FalcornError::runtime("Invalid control subscribe frame"));
    }

    let subscribe: control_ipc::ControlSubscribeRequest = bincode::deserialize(&payload)
        .map_err(|e| FalcornError::runtime(format!("Invalid control subscribe payload: {}", e)))?;

    if let Some(expected) = auth_token {
        let provided = subscribe.auth_token.as_deref().ok_or_else(|| {
            FalcornError::runtime("Missing auth token in control subscribe request")
        })?;
        if provided != expected {
            let err = control_ipc::ControlErrorFrame {
                code: "authentication_failed".to_string(),
                message: "Invalid control auth token".to_string(),
            };
            let _ = control_ipc::write_typed_frame(
                &mut stream,
                control_ipc::ControlFrameType::Error,
                &err,
            );
            return Err(FalcornError::runtime("Invalid control auth token"));
        }
    }

    let ack = control_ipc::ControlSubscribeAck {
        protocol_version: control_ipc::CONTROL_PROTOCOL_VERSION,
    };
    control_ipc::write_typed_frame(&mut stream, control_ipc::ControlFrameType::Ack, &ack)?;

    stream
        .set_read_timeout(None)
        .map_err(FalcornError::IoError)?;

    loop {
        let (frame_type, payload) = match control_ipc::read_frame(&mut stream) {
            Ok(v) => v,
            Err(e) => {
                if let FalcornError::IoError(ref io_err) = e {
                    if io_err.kind() == std::io::ErrorKind::UnexpectedEof {
                        return Ok(());
                    }
                    if io_err.kind() == std::io::ErrorKind::WouldBlock {
                        std::thread::sleep(Duration::from_millis(50));
                        continue;
                    }
                }
                return Err(e);
            }
        };

        match frame_type {
            control_ipc::ControlFrameType::ActionRequest => {
                let request: control_ipc::ActionRequest =
                    bincode::deserialize(&payload).map_err(|e| {
                        FalcornError::runtime(format!(
                            "Invalid control action request payload: {}",
                            e
                        ))
                    })?;
                let response = dispatch_action(request, config, &snapshots, &commands, start_time);
                control_ipc::write_typed_frame(
                    &mut stream,
                    control_ipc::ControlFrameType::ActionResponse,
                    &response,
                )?;
            }
            control_ipc::ControlFrameType::Ping => {
                let ping: control_ipc::ControlPingFrame =
                    bincode::deserialize(&payload).map_err(|e| {
                        FalcornError::runtime(format!("Invalid control ping payload: {}", e))
                    })?;
                let pong = control_ipc::ControlPongFrame {
                    ts_millis: ping.ts_millis,
                };
                control_ipc::write_typed_frame(
                    &mut stream,
                    control_ipc::ControlFrameType::Pong,
                    &pong,
                )?;
            }
            control_ipc::ControlFrameType::Pong => {}
            _ => {
                let err = control_ipc::ControlErrorFrame {
                    code: "unexpected_frame".to_string(),
                    message: "Only action/ping/pong frames are accepted after subscribe"
                        .to_string(),
                };
                let _ = control_ipc::write_typed_frame(
                    &mut stream,
                    control_ipc::ControlFrameType::Error,
                    &err,
                );
                break;
            }
        }
    }

    Ok(())
}

fn dispatch_action(
    request: control_ipc::ActionRequest,
    config: &Arc<Mutex<ServerConfig>>,
    snapshots: &Arc<Mutex<WorkerSnapshotState>>,
    commands: &Arc<Mutex<std::collections::VecDeque<control_ipc::ControlCommand>>>,
    start_time: Instant,
) -> control_ipc::ActionResponse {
    match request.action {
        control_ipc::ActionKind::GetStatus => {
            let config_guard = config.lock().expect("config mutex poisoned");
            let payload = control_ipc::ActionPayload::Status(control_ipc::StatusSnapshot {
                version: env!("CARGO_PKG_VERSION").to_string(),
                pid: std::process::id(),
                bind_addr: config_guard.bind_addr(),
                worker_count: snapshots
                    .lock()
                    .map(|guard| guard.workers.len())
                    .unwrap_or(0),
                worker_class: config_guard.worker_class.as_str().to_string(),
                app_module: config_guard.app_module.clone(),
                app_callable: config_guard.app_callable.clone(),
                uptime_secs: start_time.elapsed().as_secs(),
            });
            control_ipc::ActionResponse {
                id: request.id,
                status: control_ipc::ActionStatus::Ok,
                payload: Some(payload),
                error: None,
            }
        }
        control_ipc::ActionKind::GetWorkers => {
            let workers = snapshots
                .lock()
                .map(|guard| guard.workers.clone())
                .unwrap_or_default();
            let payload = control_ipc::ActionPayload::Workers(workers);
            control_ipc::ActionResponse {
                id: request.id,
                status: control_ipc::ActionStatus::Ok,
                payload: Some(payload),
                error: None,
            }
        }
        control_ipc::ActionKind::ShowConfig => {
            let config_guard = config.lock().expect("config mutex poisoned");
            let serialized = toml::to_string_pretty(&*config_guard)
                .unwrap_or_else(|_| "<failed_to_serialize>".to_string());
            control_ipc::ActionResponse {
                id: request.id,
                status: control_ipc::ActionStatus::Ok,
                payload: Some(control_ipc::ActionPayload::Config(serialized)),
                error: None,
            }
        }
        control_ipc::ActionKind::ScaleTo => {
            let Some(control_ipc::ActionRequestPayload::ScaleTo(req)) = request.payload else {
                return action_error(request.id, "missing_payload", "ScaleTo payload required");
            };
            enqueue_command(commands, request.id, |tx| {
                control_ipc::ControlCommand::ScaleTo {
                    workers: req.workers,
                    respond_to: tx,
                }
            })
        }
        control_ipc::ActionKind::RestartWorker => {
            let Some(control_ipc::ActionRequestPayload::RestartWorker(req)) = request.payload
            else {
                return action_error(
                    request.id,
                    "missing_payload",
                    "RestartWorker payload required",
                );
            };
            enqueue_command(commands, request.id, |tx| {
                control_ipc::ControlCommand::RestartWorker {
                    id: req.id,
                    graceful: req.graceful,
                    respond_to: tx,
                }
            })
        }
        control_ipc::ActionKind::Shutdown => {
            let Some(control_ipc::ActionRequestPayload::Shutdown(req)) = request.payload else {
                return action_error(request.id, "missing_payload", "Shutdown payload required");
            };
            enqueue_command(commands, request.id, |tx| {
                control_ipc::ControlCommand::Shutdown {
                    graceful: req.graceful,
                    respond_to: tx,
                }
            })
        }
        control_ipc::ActionKind::ReloadConfig => {
            let Some(control_ipc::ActionRequestPayload::ReloadConfig(req)) = request.payload else {
                return action_error(
                    request.id,
                    "missing_payload",
                    "ReloadConfig payload required",
                );
            };
            let path = match req.path {
                Some(p) => p,
                None => {
                    let config_guard = config.lock().expect("config mutex poisoned");
                    match config_guard.config_path.clone() {
                        Some(p) => p,
                        None => {
                            return action_error(
                                request.id,
                                "missing_config_path",
                                "No config path available; provide a path",
                            );
                        }
                    }
                }
            };
            enqueue_command(commands, request.id, |tx| {
                control_ipc::ControlCommand::ReloadConfig {
                    path,
                    rolling: req.rolling,
                    respond_to: tx,
                }
            })
        }
    }
}

fn enqueue_command<F>(
    commands: &Arc<Mutex<std::collections::VecDeque<control_ipc::ControlCommand>>>,
    request_id: u64,
    build: F,
) -> control_ipc::ActionResponse
where
    F: FnOnce(mpsc::Sender<control_ipc::CommandResult>) -> control_ipc::ControlCommand,
{
    let (tx, rx) = mpsc::channel();
    let command = build(tx);

    if let Ok(mut guard) = commands.lock() {
        guard.push_back(command);
    } else {
        return action_error(request_id, "enqueue_failed", "Failed to enqueue command");
    }

    match rx.recv_timeout(Duration::from_secs(2)) {
        Ok(result) => {
            if result.ok {
                control_ipc::ActionResponse {
                    id: request_id,
                    status: control_ipc::ActionStatus::Ok,
                    payload: Some(control_ipc::ActionPayload::Message(result.message)),
                    error: None,
                }
            } else {
                action_error(request_id, "command_failed", &result.message)
            }
        }
        Err(_) => action_error(request_id, "timeout", "Command timed out"),
    }
}

fn action_error(id: u64, code: &str, message: &str) -> control_ipc::ActionResponse {
    control_ipc::ActionResponse {
        id,
        status: control_ipc::ActionStatus::Error,
        payload: None,
        error: Some(control_ipc::ActionError {
            code: code.to_string(),
            message: message.to_string(),
        }),
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_server_creation() {
        let config = ServerConfig::default();
        let server = FalcornServer::new(config);
        assert!(server.is_ok());
    }

    #[test]
    fn test_worker_creation() {
        let config = ServerConfig::default();
        let worker = FalcornServer::new_worker(config, 0);
        assert!(worker.is_ok());
    }
}
