use r2d2::{Pool, PooledConnection};
use r2d2_postgres::postgres::NoTls;
use r2d2_postgres::PostgresConnectionManager;
use std::sync::Arc;

use crate::config::Config;

pub type PgPool = Pool<PostgresConnectionManager<NoTls>>;
pub type PgConn = PooledConnection<PostgresConnectionManager<NoTls>>;

#[derive(Clone)]
pub struct AppState {
    pub config: Arc<Config>,
    pub pool: PgPool,
}

impl AppState {
    pub fn new(config: Config) -> anyhow::Result<Self> {
        let manager = PostgresConnectionManager::new(config.database_url.parse()?, NoTls);
        let pool = Pool::builder()
            .max_size(10)
            .min_idle(Some(2))
            .build(manager)?;

        Ok(Self {
            config: Arc::new(config),
            pool,
        })
    }

    pub fn get_conn(&self) -> anyhow::Result<PgConn> {
        self.pool.get().map_err(|e| e.into())
    }
}

pub async fn get_user_id(
    state: &axum::extract::State<AppState>,
    header: &axum::http::HeaderMap,
) -> anyhow::Result<i32> {
    let cookie = header.get("Cookie").ok_or(anyhow::anyhow!("No cookie"))?;
    let cookie_str = cookie.to_str()?;

    let session_id = cookie_str
        .split(';')
        .find_map(|c: &str| {
            let c = c.trim();
            if c.starts_with("session_id=") {
                Some(c.trim_start_matches("session_id=").to_string())
            } else {
                None
            }
        })
        .ok_or(anyhow::anyhow!("No session cookie"))?;

    let mut conn = state.get_conn()?;
    let row = postgres::GenericClient::query_one(
        &mut *conn,
        "SELECT user_id FROM sessions WHERE id = $1 AND expires_at > CURRENT_TIMESTAMP",
        &[&session_id],
    )?;

    Ok(row.get(0))
}
