// Python callback for wsgi call (start_rresponse)

use crossbeam_queue::SegQueue;
use pyo3::prelude::*;
use std::sync::Arc;
use std::sync::atomic::{AtomicBool, Ordering};

pub struct ResponseState {
    pub status: SegQueue<String>,
    pub headers: SegQueue<(String, String)>,
    pub called: AtomicBool,
}

impl ResponseState {
    pub fn new() -> Self {
        Self {
            status: SegQueue::new(),
            headers: SegQueue::new(),
            called: AtomicBool::new(false),
        }
    }
}

#[pyclass]
pub struct StartResponse {
    pub state: Arc<ResponseState>,
}

#[pymethods]
impl StartResponse {
    fn __call__(&self, status: String, headers: Vec<(String, String)>) -> PyResult<()> {
        if self.state.called.swap(true, Ordering::Relaxed) {
            return Err(pyo3::exceptions::PyRuntimeError::new_err(
                "start_response called twice",
            ));
        }

        self.state.status.push(status);
        for h in headers {
            self.state.headers.push(h);
        }

        Ok(())
    }
}

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

    #[test]
    fn test_start_response_sets_state() {
        let state = Arc::new(ResponseState::new());
        let start = StartResponse {
            state: Arc::clone(&state),
        };

        start
            .__call__(
                "200 OK".to_string(),
                vec![("content-type".to_string(), "text/plain".to_string())],
            )
            .expect("start_response should succeed");

        assert!(state.called.load(Ordering::Relaxed));
        assert_eq!(state.status.pop().as_deref(), Some("200 OK"));
        assert_eq!(
            state.headers.pop(),
            Some(("content-type".to_string(), "text/plain".to_string()))
        );
    }

    #[test]
    fn test_start_response_cannot_be_called_twice() {
        let state = Arc::new(ResponseState::new());
        let start = StartResponse {
            state: Arc::clone(&state),
        };

        start
            .__call__("200 OK".to_string(), vec![])
            .expect("first call must succeed");
        let err = start
            .__call__("200 OK".to_string(), vec![])
            .expect_err("second call must fail");
        assert!(err.to_string().contains("start_response called twice"));
    }
}
