use crate::db::DbPool;
use chrono::Utc;
use serde::{Deserialize, Serialize};
use uuid::Uuid;

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UserPoolClient {
    pub client_id: String,
    pub pool_id: String,
    pub client_name: String,
    pub config_json: serde_json::Value,
    pub created_at: String,
    pub updated_at: String,
}

pub fn create(
    db: &DbPool,
    pool_id: &str,
    client_name: &str,
    config: &serde_json::Value,
) -> rusqlite::Result<UserPoolClient> {
    let conn = db.lock().unwrap();
    let client_id = Uuid::new_v4().to_string().replace('-', "")[..26].to_string();
    let now = Utc::now().to_rfc3339();
    let config_str = serde_json::to_string(config).unwrap_or_default();
    conn.execute(
        "INSERT INTO user_pool_clients (client_id, pool_id, client_name, config_json, created_at, updated_at) VALUES (?1, ?2, ?3, ?4, ?5, ?6)",
        rusqlite::params![client_id, pool_id, client_name, config_str, now, now],
    )?;
    Ok(UserPoolClient {
        client_id,
        pool_id: pool_id.to_string(),
        client_name: client_name.to_string(),
        config_json: config.clone(),
        created_at: now.clone(),
        updated_at: now,
    })
}

pub fn get(db: &DbPool, pool_id: &str, client_id: &str) -> rusqlite::Result<Option<UserPoolClient>> {
    let conn = db.lock().unwrap();
    let mut stmt = conn.prepare(
        "SELECT client_id, pool_id, client_name, config_json, created_at, updated_at FROM user_pool_clients WHERE pool_id = ?1 AND client_id = ?2"
    )?;
    let mut rows = stmt.query(rusqlite::params![pool_id, client_id])?;
    match rows.next()? {
        Some(row) => {
            let config_str: String = row.get(3)?;
            Ok(Some(UserPoolClient {
                client_id: row.get(0)?,
                pool_id: row.get(1)?,
                client_name: row.get(2)?,
                config_json: serde_json::from_str(&config_str).unwrap_or_default(),
                created_at: row.get(4)?,
                updated_at: row.get(5)?,
            }))
        }
        None => Ok(None),
    }
}

pub fn list(db: &DbPool, pool_id: &str) -> rusqlite::Result<Vec<UserPoolClient>> {
    let conn = db.lock().unwrap();
    let mut stmt = conn.prepare(
        "SELECT client_id, pool_id, client_name, config_json, created_at, updated_at FROM user_pool_clients WHERE pool_id = ?1 ORDER BY created_at"
    )?;
    let rows = stmt.query_map(rusqlite::params![pool_id], |row| {
        let config_str: String = row.get(3)?;
        Ok(UserPoolClient {
            client_id: row.get(0)?,
            pool_id: row.get(1)?,
            client_name: row.get(2)?,
            config_json: serde_json::from_str(&config_str).unwrap_or_default(),
            created_at: row.get(4)?,
            updated_at: row.get(5)?,
        })
    })?;
    rows.collect()
}

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

    fn setup() -> DbPool {
        let pool = db::connect_in_memory().unwrap();
        db::run_migrations(&pool).unwrap();
        db::pools::create(&pool, "us-east-1_test", "Test", &serde_json::json!({})).unwrap();
        pool
    }

    #[test]
    fn test_create_and_get_client() {
        let db = setup();
        let client = create(&db, "us-east-1_test", "my-client", &serde_json::json!({"ExplicitAuthFlows": ["ALLOW_USER_PASSWORD_AUTH"]})).unwrap();
        assert_eq!(client.client_name, "my-client");
        assert!(!client.client_id.is_empty());

        let fetched = get(&db, "us-east-1_test", &client.client_id).unwrap().unwrap();
        assert_eq!(fetched.client_name, "my-client");
    }

    #[test]
    fn test_list_clients() {
        let db = setup();
        create(&db, "us-east-1_test", "client-a", &serde_json::json!({})).unwrap();
        create(&db, "us-east-1_test", "client-b", &serde_json::json!({})).unwrap();
        let clients = list(&db, "us-east-1_test").unwrap();
        assert_eq!(clients.len(), 2);
    }
}
