use std::sync::Arc;

use axum::extract::FromRef;
use axum_extra::extract::cookie::Key;
use sha2::{Digest, Sha512};
use sqlx::PgPool;
use tokio::sync::OnceCell;

use crate::auth::OidcDiscovery;
use crate::config::Config;

#[derive(Clone)]
pub struct AppState {
    pub db: PgPool,
    pub s3: aws_sdk_s3::Client,
    pub http: reqwest::Client,
    pub config: Arc<Config>,
    pub cookie_key: Key,
    pub oidc: Arc<OnceCell<OidcDiscovery>>,
    /// Caps concurrent zip streams — each holds S3 connections for its
    /// duration, and slow readers would otherwise pin them indefinitely.
    pub zip_permits: Arc<tokio::sync::Semaphore>,
}

impl FromRef<AppState> for Key {
    fn from_ref(state: &AppState) -> Key {
        state.cookie_key.clone()
    }
}

impl AppState {
    pub async fn new(config: Config) -> anyhow::Result<Self> {
        let db = sqlx::postgres::PgPoolOptions::new()
            .max_connections(10)
            .connect(&config.database_url)
            .await?;
        sqlx::migrate!("./migrations").run(&db).await?;
        let s3 = crate::s3::client(&config);
        // SHA-512 digest is exactly the 64 bytes Key::from requires.
        let cookie_key = Key::from(&Sha512::digest(config.session_secret.as_bytes()));
        Ok(Self {
            db,
            s3,
            http: reqwest::Client::new(),
            config: Arc::new(config),
            cookie_key,
            oidc: Arc::new(OnceCell::new()),
            zip_permits: Arc::new(tokio::sync::Semaphore::new(4)),
        })
    }
}
