//! Process Worker - Independent Worker with iits own process, GIL and app instance.

use crate::config::ServerConfig;
use crate::errors::{FalcornError, Result};
use crate::http::HttpEngine;
use crate::logging::ipc;
use crate::logging::{Logger, start_logger_client};
use crate::python::app::ApplicationInstance;
use crate::runtime::BufferPool;
use crate::runtime::effective_max_requests;
use crate::server::request_handler::RequestHandler;
use crossbeam_channel::{Receiver, RecvTimeoutError, SendTimeoutError, bounded};
use signal_hook::consts::signal::*;
use signal_hook::iterator::Signals;
use std::net::{SocketAddr, TcpListener, TcpStream};
use std::sync::Arc;
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
use std::thread::JoinHandle;
use std::time::Duration;

// Connection Task
struct ConnTask {
    stream: TcpStream,
    addr: SocketAddr,
}

/// Sync Worker
pub struct SyncWorker {
    id: u32,
    config: Arc<ServerConfig>,
    logger: Arc<Logger>,
    app: Arc<ApplicationInstance>,
    handler: RequestHandler,
    shutdown: Arc<AtomicBool>,
    request_count: Arc<AtomicUsize>,
    active_connections: Arc<AtomicUsize>,
    pool: BufferPool,
    max_requests_limit: Option<usize>,
}

impl SyncWorker {
    /// Create new worker
    pub fn new(id: u32, config: Arc<ServerConfig>, logger: Arc<Logger>) -> Result<Self> {
        logger.info(|| {
            format!(
                "[Worker {}] Initializing process worker (PID: {})",
                id,
                std::process::id()
            )
        });

        // Load python wsgi app
        // Each worker has a dedicated python instance so a proper GIL
        let pythonpath: Vec<std::path::PathBuf> = config
            .pythonpath
            .iter()
            .map(|p| std::path::PathBuf::from(p))
            .collect();

        let app = ApplicationInstance::load(
            &config.app_module,
            &config.app_callable,
            &pythonpath,
            &config.protocol,
            &config.asgi_event_loop,
            config.asgi_event_loop_fallback,
            config.asgi_submit_timeout_ms,
            config.asgi_max_inflight,
            config.asgi_send_pool_size,
            &config.asgi_scheduler,
        )?;

        logger.info(|| {
            format!(
                "[Worker {}] Loaded Python application: {}:{}",
                id, config.app_module, config.app_callable
            )
        });

        let handler = RequestHandler::new(app.clone(), config.protocol.clone(), config.strict_http);

        let pool = BufferPool::new(config.buffer_size, 1_048_576);
        let max_requests_limit = effective_max_requests(&config, id);

        Ok(Self {
            id,
            config,
            logger,
            app,
            handler,
            shutdown: Arc::new(AtomicBool::new(false)),
            request_count: Arc::new(AtomicUsize::new(0)),
            active_connections: Arc::new(AtomicUsize::new(0)),
            pool,
            max_requests_limit,
        })
    }

    /// Run worker (main entrypoint)
    pub fn run(&mut self) -> Result<()> {
        self.logger.info(|| {
            format!(
                "[Worker {}] Starting on {} (PID: {}, max_requests={})",
                self.id,
                self.config.bind_addr(),
                std::process::id(),
                self.max_requests_limit
                    .map(|v| v.to_string())
                    .unwrap_or_else(|| "none".to_string())
            )
        });

        // Setup signals handling
        self.setup_signal_handlers()?;

        // Create a TCP listener (reuse same port with SO_REUSEPORT)
        let listener = self.create_listener()?;

        // Main Accept loop
        self.accept_loop(&listener)?;

        self.logger
            .info(|| format!("[Worker {}] Stopped (PID: {})", self.id, std::process::id()));

        Ok(())
    }

    /// Create a TCP listener with SO_REUSEPORT
    fn create_listener(&self) -> Result<TcpListener> {
        use std::os::unix::io::{FromRawFd, OwnedFd};

        let fd_str = std::env::var("FALCORN_LISTEN_FD")
            .map_err(|_| FalcornError::runtime("FALCORN_LISTEN_FD not set"))?;

        let fd: i32 = fd_str
            .parse()
            .map_err(|_| FalcornError::runtime("Invalid FALCORN_LISTEN_FD"))?;

        // IMPORTANT:
        // The listen FD is inherited from the master and may be shared across workers.
        // We must `dup()` it so that each worker owns its own FD handle; otherwise dropping
        // the listener in one worker can close the shared FD and cause immediate EOFs.
        let dup_fd = unsafe { libc::dup(fd) };
        if dup_fd < 0 {
            return Err(FalcornError::runtime("Failed to dup FALCORN_LISTEN_FD"));
        }

        let owned = unsafe { OwnedFd::from_raw_fd(dup_fd) };
        let listener = std::net::TcpListener::from(owned);

        self.logger.debug(|| {
            format!(
                "[Worker {}] Listening on {} (inherited fd={}, dup fd={})",
                self.id,
                self.config.bind_addr(),
                fd,
                dup_fd
            )
        });

        Ok(listener)
    }

    /// Setup signals handlers
    fn setup_signal_handlers(&self) -> Result<()> {
        let shutdown = Arc::clone(&self.shutdown);
        let id = self.id;
        let logger = Arc::clone(&self.logger);

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

            for sig in signals.forever() {
                match sig {
                    SIGTERM | SIGINT => {
                        logger
                            .info(|| format!("[Worker {}] Received shutdown signal ({})", id, sig));
                        shutdown.store(true, Ordering::Relaxed);
                        break;
                    }
                    SIGQUIT => {
                        logger.info(|| format!("[Worker {}] Received quit signal", id));
                        shutdown.store(true, Ordering::Relaxed);
                        break;
                    }
                    _ => {}
                }
            }
        });

        Ok(())
    }

    /// Accept connection loop with bounded handler pool
    fn accept_loop(&mut self, listener: &TcpListener) -> Result<()> {
        listener.set_nonblocking(false)?;

        let handler_threads = self.config.sync_handler_threads;
        let queue_capacity = self.config.sync_conn_queue_capacity;
        let (tx, rx) = bounded::<ConnTask>(queue_capacity);
        let handler_handles = self.spawn_handler_pool(rx, handler_threads)?;

        self.start_metrics_reporter();

        self.logger.info(|| {
            format!(
                "[Worker {}] Sync handler pool ready (threads={}, queue_capacity={})",
                self.id, handler_threads, queue_capacity
            )
        });

        let mut consecutive_errors = 0;
        const MAX_CONSECUTIVE_ERRORS: u32 = 10;

        while !self.shutdown.load(Ordering::Relaxed) {
            if self.max_requests_reached() {
                self.logger.info(|| {
                    format!(
                        "[Worker {}] Max requests reached, stopping accept loop",
                        self.id
                    )
                });
                self.shutdown.store(true, Ordering::Relaxed);
                break;
            }

            match listener.accept() {
                Ok((stream, addr)) => {
                    consecutive_errors = 0;
                    let max_conn = self.config.max_connections.max(1);
                    let current = self.active_connections.fetch_add(1, Ordering::Relaxed) + 1;
                    if current > max_conn {
                        self.active_connections.fetch_sub(1, Ordering::Relaxed);
                        self.logger.debug(|| {
                            format!(
                                "[Worker {}] Connection refused (max_connections reached)",
                                self.id
                            )
                        });
                        continue;
                    }
                    match tx.send_timeout(ConnTask { stream, addr }, Duration::from_micros(300)) {
                        Ok(_) => {}
                        Err(SendTimeoutError::Timeout(task)) => {
                            self.active_connections.fetch_sub(1, Ordering::Relaxed);
                            self.logger.debug(|| {
                                format!(
                                    "[Worker {}] Connection queue full, dropping {}",
                                    self.id, task.addr
                                )
                            });
                            drop(task);
                        }
                        Err(SendTimeoutError::Disconnected(_task)) => {
                            self.active_connections.fetch_sub(1, Ordering::Relaxed);
                            return Err(FalcornError::runtime(
                                "Sync handler queue disconnected unexpectedly",
                            ));
                        }
                    }
                }
                Err(e) => {
                    if e.kind() == std::io::ErrorKind::Interrupted {
                        continue;
                    }

                    consecutive_errors += 1;

                    if !self.shutdown.load(Ordering::Relaxed) {
                        self.logger.error(|| {
                            format!(
                                "[Worker {}] Accept error (#{}/{}): {}",
                                self.id, consecutive_errors, MAX_CONSECUTIVE_ERRORS, e
                            )
                        });

                        if consecutive_errors >= MAX_CONSECUTIVE_ERRORS {
                            return Err(FalcornError::runtime(format!(
                                "Too many consecutive accept errors: {}",
                                e
                            )));
                        }

                        // Exponential Backoff
                        std::thread::sleep(Duration::from_millis(100 * consecutive_errors as u64));
                    } else {
                        break;
                    }
                }
            }
        }

        drop(tx);

        for handle in handler_handles {
            if let Err(e) = handle.join() {
                self.logger
                    .error(|| format!("[Worker {}] Handler thread panic: {:?}", self.id, e));
            }
        }

        Ok(())
    }

    fn start_metrics_reporter(&self) {
        if !self.config.metrics_workers_enabled {
            return;
        }
        let logger = self.logger.clone();
        let active_connections = Arc::clone(&self.active_connections);
        let request_count = Arc::clone(&self.request_count);
        let shutdown = Arc::clone(&self.shutdown);
        std::thread::spawn(move || {
            let interval = Duration::from_secs(1);
            while !shutdown.load(Ordering::Relaxed) {
                let active = active_connections.load(Ordering::Relaxed);
                let total_requests = request_count.load(Ordering::Relaxed) as u64;
                logger.worker_stats(active, total_requests);
                std::thread::sleep(interval);
            }
        });
    }

    fn spawn_handler_pool(
        &self,
        rx: Receiver<ConnTask>,
        handler_threads: usize,
    ) -> Result<Vec<JoinHandle<()>>> {
        let mut handles = Vec::with_capacity(handler_threads);

        for idx in 0..handler_threads {
            let rx = rx.clone();
            let config = Arc::clone(&self.config);
            let logger = Arc::clone(&self.logger);
            let shutdown = Arc::clone(&self.shutdown);
            let handler = self.handler.clone();
            let request_count = Arc::clone(&self.request_count);
            let active_connections = Arc::clone(&self.active_connections);
            let pool = self.pool.clone();
            let max_requests_limit = self.max_requests_limit;
            let worker_id = self.id;

            let handle = std::thread::Builder::new()
                .name(format!("falcorn-sync-{}-{}", worker_id, idx))
                .spawn(move || {
                    loop {
                        if shutdown.load(Ordering::Relaxed) && rx.is_empty() {
                            break;
                        }

                        if let Some(max) = max_requests_limit {
                            if request_count.load(Ordering::Relaxed) >= max && rx.is_empty() {
                                break;
                            }
                        }

                        match rx.recv_timeout(Duration::from_millis(300)) {
                            Ok(task) => Self::handle_conn_task(
                                worker_id,
                                &config,
                                &logger,
                                &handler,
                                &request_count,
                                &active_connections,
                                &pool,
                                task,
                            ),
                            Err(RecvTimeoutError::Timeout) => continue,
                            Err(RecvTimeoutError::Disconnected) => break,
                        }
                    }
                })
                .map_err(|e| {
                    FalcornError::runtime(format!(
                        "Failed to spawn sync handler thread {}: {}",
                        idx, e
                    ))
                })?;

            handles.push(handle);
        }

        Ok(handles)
    }

    fn handle_conn_task(
        worker_id: u32,
        config: &ServerConfig,
        logger: &Logger,
        handler: &RequestHandler,
        request_count: &Arc<AtomicUsize>,
        active_connections: &Arc<AtomicUsize>,
        pool: &BufferPool,
        task: ConnTask,
    ) {
        struct ConnGuard<'a> {
            counter: &'a Arc<AtomicUsize>,
        }
        impl<'a> Drop for ConnGuard<'a> {
            fn drop(&mut self) {
                self.counter.fetch_sub(1, Ordering::Relaxed);
            }
        }
        let _guard = ConnGuard {
            counter: active_connections,
        };

        let _ = task.stream.set_nonblocking(false);
        let _ = task
            .stream
            .set_read_timeout(Some(Duration::from_secs(config.worker_timeout)));
        let _ = task
            .stream
            .set_write_timeout(Some(Duration::from_secs(config.worker_timeout)));
        let _ = task.stream.set_nodelay(true);

        let engine = HttpEngine::with_pool(pool.clone());
        if let Err(e) = engine.handle_connection_sync(
            task.stream,
            task.addr,
            handler,
            logger,
            config,
            Some(request_count.clone()),
        ) {
            if e.is_recoverable() {
                logger.warn(|| format!("[Worker {}] Recoverable error: {}", worker_id, e));
            } else {
                logger.error(|| format!("[Worker {}] Error handling connection: {}", worker_id, e));
            }
        }
    }

    fn max_requests_reached(&self) -> bool {
        self.max_requests_limit
            .map(|max| self.request_count.load(Ordering::Relaxed) >= max)
            .unwrap_or(false)
    }
}

/// Worker mode entry
pub fn run_worker_process(worker_id: u32, config: ServerConfig) -> Result<()> {
    // Create a logger for the worker
    let socket = ipc::socket_path();
    let tx = start_logger_client(&socket);
    let logger = Arc::new(
        Logger::new(config.log_level_enum(), tx, config.log_stdout)
            .with_access_enabled(config.access_events_enabled())
            .with_worker_id(worker_id),
    );

    logger.info(|| {
        format!(
            "[Worker {}] Process started (PID: {})",
            worker_id,
            std::process::id()
        )
    });

    // Create and run worker
    let mut worker = SyncWorker::new(worker_id, Arc::new(config), logger.clone())?;

    let result = worker.run();

    logger.info(|| {
        format!(
            "[Worker {}] Process exiting (PID: {})",
            worker_id,
            std::process::id()
        )
    });

    result
}
