use sqlx::PgPool;
use uuid::Uuid;

use crate::models::Session;

pub async fn create_session(
    pool: &PgPool,
    user_id: Uuid,
    token_hash: &str,
    ip_address: Option<&str>,
    user_agent: Option<&str>,
    expires_at: &chrono::DateTime<chrono::Utc>,
) -> Result<Session, sqlx::Error> {
    let ip_network = ip_address
        .and_then(|ip| ip.parse::<sqlx::types::ipnetwork::IpNetwork>().ok());

    sqlx::query_as::<_, Session>(
        r#"
        INSERT INTO sessions (user_id, token_hash, ip_address, user_agent, is_active, expires_at)
        VALUES ($1, $2, $3, $4, TRUE, $5)
        RETURNING *
        "#,
    )
    .bind(user_id)
    .bind(token_hash)
    .bind(ip_network)
    .bind(user_agent)
    .bind(expires_at)
    .fetch_one(pool)
    .await
}

pub async fn invalidate_session(pool: &PgPool, token_hash: &str) -> Result<(), sqlx::Error> {
    sqlx::query(
        r#"
        UPDATE sessions
        SET is_active = FALSE
        WHERE token_hash = $1
        "#,
    )
    .bind(token_hash)
    .execute(pool)
    .await?;
    Ok(())
}

pub async fn invalidate_user_sessions(pool: &PgPool, user_id: Uuid) -> Result<(), sqlx::Error> {
    sqlx::query(
        r#"
        UPDATE sessions
        SET is_active = FALSE
        WHERE user_id = $1 AND is_active = TRUE
        "#,
    )
    .bind(user_id)
    .execute(pool)
    .await?;
    Ok(())
}

pub async fn find_active_session(
    pool: &PgPool,
    token_hash: &str,
) -> Result<Option<Session>, sqlx::Error> {
    sqlx::query_as::<_, Session>(
        r#"
        SELECT * FROM sessions
        WHERE token_hash = $1 AND is_active = TRUE AND expires_at > NOW()
        "#,
    )
    .bind(token_hash)
    .fetch_optional(pool)
    .await
}
