//! Global state and utility functions for LogXide
//!
//! This module contains global registries, thread-local storage,
//! and module-level utility functions.

use pyo3::prelude::*;
use pyo3::types::{PyAny, PyDict};
use std::cell::RefCell;
use std::collections::HashMap;
use std::sync::{Arc, Mutex};

use once_cell::sync::Lazy;

use crate::core::{get_logger as core_get_logger, get_root_logger, LogLevel};
use crate::fast_logger;
use crate::handler::{FileHandler, HTTPHandler, Handler, OverflowStrategy, RotatingFileHandler};
use crate::formatter::PythonFormatter;
use crate::py_handlers::{
    PyFileHandler, PyHTTPHandler, PyMemoryHandler, PyOTLPHandler, PyRotatingFileHandler,
    PyStreamHandler,
};
use crate::py_logger::PyLogger;

/// Global registry of log handlers.
pub static HANDLERS: Lazy<Mutex<Vec<Arc<dyn Handler + Send + Sync>>>> =
    Lazy::new(|| Mutex::new(Vec::new()));

/// GLOBAL KEEP ALIVE to prevent Python objects from being garbage collected
pub static PYTHON_HANDLERS_KEEP_ALIVE: Lazy<Mutex<Vec<Py<PyAny>>>> =
    Lazy::new(|| Mutex::new(Vec::new()));

/// GLOBAL LOGGER REGISTRY in Rust to keep PyLogger objects alive
pub static PY_LOGGER_KEEP_ALIVE: Lazy<Mutex<HashMap<String, Py<PyLogger>>>> =
    Lazy::new(|| Mutex::new(HashMap::new()));

thread_local! {
    pub static THREAD_NAME: RefCell<Option<String>> = const { RefCell::new(None) };
}

#[pyfunction(name = "getLogger")]
#[pyo3(signature = (name=None, manager=None))]
pub fn get_logger(
    py: Python,
    name: Option<&str>,
    manager: Option<Py<PyAny>>,
) -> PyResult<PyLogger> {
    let logger_name = name.unwrap_or("root");

    let mut alive = PY_LOGGER_KEEP_ALIVE.lock().unwrap();
    if let Some(p) = alive.get(logger_name) {
        return Ok(p.bind(py).borrow().clone());
    }

    let inner = if name.is_some() {
        core_get_logger(logger_name)
    } else {
        get_root_logger()
    };
    let pylogger = PyLogger::with_params(
        inner,
        fast_logger::get_fast_logger(logger_name),
        manager.map(|m| m.clone_ref(py)),
    );

    let p = Py::new(py, pylogger)?;
    alive.insert(logger_name.to_string(), p.clone_ref(py));

    Ok(p.bind(py).borrow().clone())
}

#[pyfunction]
#[pyo3(signature = (**_kwargs))]
pub fn basicConfig(_py: Python, _kwargs: Option<&Bound<'_, PyDict>>) -> PyResult<()> {
    Ok(())
}

#[pyfunction]
pub fn flush(_py: Python) -> PyResult<()> {
    let handlers = HANDLERS.lock().unwrap();
    for h in handlers.iter() {
        futures::executor::block_on(h.flush());
    }
    Ok(())
}

#[pyfunction]
pub fn set_thread_name(_py: Python, name: String) -> PyResult<()> {
    THREAD_NAME.with(|n| {
        *n.borrow_mut() = Some(name);
    });
    Ok(())
}

#[pyfunction]
#[pyo3(signature = (url, headers=None, capacity=None, batch_size=None, flush_interval=None, level=None))]
pub fn register_http_handler(
    _py: Python,
    url: String,
    headers: Option<HashMap<String, String>>,
    capacity: Option<usize>,
    batch_size: Option<usize>,
    flush_interval: Option<u64>,
    level: Option<u32>,
) -> PyResult<()> {
    let h = Arc::new(HTTPHandler::new(
        url,
        headers.unwrap_or_default(),
        capacity.unwrap_or(10000),
        batch_size.unwrap_or(1000),
        flush_interval.unwrap_or(30),
        OverflowStrategy::DropOldest,
    ));
    h.set_level(LogLevel::from_usize(level.unwrap_or(20) as usize));
    HANDLERS.lock().unwrap().push(h);
    Ok(())
}

#[pyfunction]
pub fn clear_handlers(_py: Python) -> PyResult<()> {
    HANDLERS.lock().unwrap().clear();
    Ok(())
}

#[pyfunction(name = "register_file_handler")]
#[pyo3(signature = (filename, level=None, format=None, datefmt=None))]
pub fn register_file_handler(
    _py: Python,
    filename: String,
    level: Option<u32>,
    format: Option<String>,
    datefmt: Option<String>,
) -> PyResult<()> {
    use pyo3::exceptions::PyValueError;

    let log_level = LogLevel::from_usize(level.unwrap_or(10) as usize);

    let handler = FileHandler::new(filename)
        .map_err(|e| PyValueError::new_err(format!("Failed to create file handler: {}", e)))?;

    handler.set_level(log_level);

    // Set formatter if format string is provided
    if let Some(fmt) = format {
        let formatter = match datefmt {
            Some(df) => PythonFormatter::with_date_format(fmt, df),
            None => PythonFormatter::new(fmt),
        };
        handler.set_formatter_instance(Arc::new(formatter));
    }

    HANDLERS.lock().unwrap().push(Arc::new(handler));
    Ok(())
}

#[pyfunction(name = "register_rotating_file_handler")]
#[pyo3(signature = (filename, max_bytes=None, backup_count=None, level=None))]
pub fn register_rotating_file_handler(
    _py: Python,
    filename: String,
    max_bytes: Option<u64>,
    backup_count: Option<u32>,
    level: Option<u32>,
) -> PyResult<()> {
    let log_level = LogLevel::from_usize(level.unwrap_or(10) as usize);

    let handler = RotatingFileHandler::new(
        filename,
        max_bytes.unwrap_or(10 * 1024 * 1024),
        backup_count.unwrap_or(5),
    ).map_err(|e| pyo3::exceptions::PyIOError::new_err(e.to_string()))?;

    handler.set_level(log_level);
    HANDLERS.lock().unwrap().push(Arc::new(handler));
    Ok(())
}

#[pyfunction(name = "register_stream_handler")]
#[pyo3(signature = (stream=None, level=None, format=None, datefmt=None))]
pub fn register_stream_handler(
    _py: Python,
    stream: Option<&Bound<PyAny>>,
    level: Option<u32>,
    format: Option<String>,
    datefmt: Option<String>,
) -> PyResult<()> {
    use pyo3::exceptions::PyValueError;

    let log_level = LogLevel::from_usize(level.unwrap_or(10) as usize);

    // Create formatter if format string is provided
    let formatter: Option<Arc<dyn crate::formatter::Formatter + Send + Sync>> = format.map(|fmt| {
        let f: Arc<dyn crate::formatter::Formatter + Send + Sync> = match datefmt {
            Some(df) => Arc::new(PythonFormatter::with_date_format(fmt, df)),
            None => Arc::new(PythonFormatter::new(fmt)),
        };
        f
    });

    if let Some(stream_obj) = stream {
        // Try to extract as string first
        if let Ok(stream_str) = stream_obj.extract::<String>() {
            // String path: "stdout" or "stderr"
            let handler = match stream_str.as_str() {
                "stdout" => crate::handler::StreamHandler::stdout(),
                "stderr" => crate::handler::StreamHandler::stderr(),
                _ => {
                    return Err(PyValueError::new_err(
                        "stream string must be 'stdout' or 'stderr'",
                    ))
                }
            };
            handler.set_level(log_level);
            if let Some(ref f) = formatter {
                handler.set_formatter_instance(f.clone());
            }
            HANDLERS.lock().unwrap().push(Arc::new(handler));
        } else {
            // For Python file-like objects, we use stderr as fallback
            // since we don't have PythonStreamHandler anymore
            let handler = crate::handler::StreamHandler::stderr();
            handler.set_level(log_level);
            if let Some(ref f) = formatter {
                handler.set_formatter_instance(f.clone());
            }
            HANDLERS.lock().unwrap().push(Arc::new(handler));
        }
    } else {
        // Default to stderr
        let handler = crate::handler::StreamHandler::stderr();
        handler.set_level(log_level);
        if let Some(ref f) = formatter {
            handler.set_formatter_instance(f.clone());
        }
        HANDLERS.lock().unwrap().push(Arc::new(handler));
    }

    Ok(())
}

/// Helper function to add a handler to the appropriate registry
pub fn add_handler_to_registry(
    handler: &Bound<PyAny>,
    logger_name: &str,
    local_handlers: &Mutex<Vec<Arc<dyn Handler + Send + Sync>>>,
    local_python_handlers: &Mutex<Vec<Py<PyAny>>>,
) -> PyResult<bool> {
    let handler_arc: Option<Arc<dyn Handler + Send + Sync>> =
        if let Ok(file_handler) = handler.extract::<PyRef<PyFileHandler>>() {
            Some(file_handler.inner.clone())
        } else if let Ok(stream_handler) = handler.extract::<PyRef<PyStreamHandler>>() {
            Some(stream_handler.inner.clone())
        } else if let Ok(rotating_handler) = handler.extract::<PyRef<PyRotatingFileHandler>>() {
            Some(rotating_handler.inner.clone())
        } else if let Ok(http_handler) = handler.extract::<PyRef<PyHTTPHandler>>() {
            Some(http_handler.inner.clone())
        } else if let Ok(otlp_handler) = handler.extract::<PyRef<PyOTLPHandler>>() {
            Some(otlp_handler.inner.clone())
        } else if let Ok(memory_handler) = handler.extract::<PyRef<PyMemoryHandler>>() {
            Some(memory_handler.inner.clone())
        } else if let Ok(inner) = handler.getattr("_inner") {
            if let Ok(file_handler) = inner.extract::<PyRef<PyFileHandler>>() {
                Some(file_handler.inner.clone())
            } else if let Ok(stream_handler) = inner.extract::<PyRef<PyStreamHandler>>() {
                Some(stream_handler.inner.clone())
            } else if let Ok(rotating_handler) = inner.extract::<PyRef<PyRotatingFileHandler>>() {
                Some(rotating_handler.inner.clone())
            } else if let Ok(http_handler) = inner.extract::<PyRef<PyHTTPHandler>>() {
                Some(http_handler.inner.clone())
            } else if let Ok(otlp_handler) = inner.extract::<PyRef<PyOTLPHandler>>() {
                Some(otlp_handler.inner.clone())
            } else if let Ok(memory_handler) = inner.extract::<PyRef<PyMemoryHandler>>() {
                Some(memory_handler.inner.clone())
            } else {
                None
            }
        } else {
            None
        };

    if let Some(h) = handler_arc {
        if logger_name == "root" {
            HANDLERS.lock().unwrap().push(h);
        } else {
            local_handlers.lock().unwrap().push(h);
        }
        // CRITICAL: Prevent Python object from being GC'd
        PYTHON_HANDLERS_KEEP_ALIVE
            .lock()
            .unwrap()
            .push(handler.clone().unbind());
        Ok(true)
    } else {
        // Allow Python handlers
        if logger_name == "root" {
            PYTHON_HANDLERS_KEEP_ALIVE
                .lock()
                .unwrap()
                .push(handler.clone().unbind());
        } else {
            local_python_handlers
                .lock()
                .unwrap()
                .push(handler.clone().unbind());
        }
        Ok(true)
    }
}
