use auth::admin::AuthAdminData;
use auth::models::AuthUserId;
use auth::public::{SessionCreateOptions, create_session_with_policy};
use auth::repositories::PostgresAuthUserRepository;
use auth::session_policy::{AuthSessionPolicy, SessionCreateDecision, SessionCreateInput};
use chrono::{Duration, Utc};
use platform_core::{AppResult, PLATFORM_MIGRATIONS, apply_migrations};
use platform_module::{AdminDataSource, AdminListQuery};
use platform_runtime::RUNTIME_MIGRATIONS;
use platform_testing::TestDatabase;
use std::sync::Arc;

#[tokio::test]
async fn session_policy_can_attach_a_canonical_device_to_created_sessions() {
    let Some(db) = TestDatabase::create().await else {
        return;
    };
    let migrations = PLATFORM_MIGRATIONS
        .iter()
        .chain(RUNTIME_MIGRATIONS)
        .chain(auth::migrations::AUTH_MIGRATIONS)
        .copied()
        .collect::<Vec<_>>();
    apply_migrations(&db.pool, &migrations)
        .await
        .expect("migrations apply");

    let now = Utc::now();
    let session = create_session_with_policy(
        &db.pool,
        &AuthUserId("usr_device".to_owned()),
        "sess_device".to_owned(),
        "token_device".to_owned(),
        now,
        now + Duration::hours(1),
        SessionCreateOptions {
            device_id: Some("browser_hint".to_owned()),
            client: platform_core::ClientRequestMetadata {
                ip: Some("203.0.113.7".to_owned()),
                user_agent: Some("LensoTest/1.0".to_owned()),
            },
        },
        &CanonicalDevicePolicy,
    )
    .await
    .expect("session should be created");

    assert_eq!(session.device_id.as_deref(), Some("device_primary"));

    let admin = AuthAdminData::new(Arc::new(PostgresAuthUserRepository::new(db.pool.clone())));
    let page = admin
        .list("sessions", &AdminListQuery::new(10, None))
        .await
        .expect("list sessions");
    assert_eq!(page.records[0]["device_id"], "device_primary");
    assert_eq!(page.records[0]["client_ip"], "203.0.113.7");
    assert_eq!(page.records[0]["user_agent"], "LensoTest/1.0");

    db.cleanup().await;
}

#[derive(Debug)]
struct CanonicalDevicePolicy;

#[async_trait::async_trait]
impl AuthSessionPolicy for CanonicalDevicePolicy {
    async fn before_session_create(
        &self,
        input: &SessionCreateInput,
    ) -> AppResult<SessionCreateDecision> {
        assert_eq!(input.proposed_device_id.as_deref(), Some("browser_hint"));
        assert_eq!(input.client.ip.as_deref(), Some("203.0.113.7"));
        Ok(SessionCreateDecision {
            device_id: Some("device_primary".to_owned()),
        })
    }
}
