#![allow(dead_code)]
#![allow(unused_imports)]
use anyhow::Result;
use futures::stream::TryStreamExt;
use mongodb::{
    bson::{doc, oid::ObjectId},
    Collection,
};
use serde::{Deserialize, Serialize};
use std::time::SystemTime;

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DeviceInfo {
    pub device_type: String, // "mobile", "desktop", "tablet"
    pub os: String,          // "iOS", "Android", "Windows", "macOS", "Linux"
    pub browser: Option<String>,
    pub ip_address: String,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Session {
    #[serde(rename = "_id", skip_serializing_if = "Option::is_none")]
    pub id: Option<ObjectId>,
    pub user_id: ObjectId,
    pub device_info: DeviceInfo,
    pub token_hash: String, // Hash of the JWT token
    pub created_at: mongodb::bson::DateTime,
    pub last_used_at: mongodb::bson::DateTime,
    pub expires_at: mongodb::bson::DateTime,
    pub is_revoked: bool,
}

#[derive(Clone)]
pub struct SessionRepository {
    collection: Collection<Session>,
}

impl SessionRepository {
    pub fn new(db: &mongodb::Database) -> Self {
        let collection = db.collection("sessions");
        Self { collection }
    }

    pub async fn create(
        &self,
        user_id: ObjectId,
        device_info: DeviceInfo,
        token_hash: String,
        duration_hours: i64,
    ) -> Result<ObjectId> {
        let now = SystemTime::now();
        let now_bson = mongodb::bson::DateTime::from(now);

        let expires_at = SystemTime::now()
            .checked_add(std::time::Duration::from_secs(duration_hours as u64 * 3600))
            .ok_or_else(|| anyhow::anyhow!("Invalid duration"))?;
        let expires_at_bson = mongodb::bson::DateTime::from(expires_at);

        let session = Session {
            id: None,
            user_id,
            device_info,
            token_hash,
            created_at: now_bson,
            last_used_at: now_bson,
            expires_at: expires_at_bson,
            is_revoked: false,
        };

        self.collection
            .insert_one(session, None)
            .await?
            .inserted_id
            .as_object_id()
            .ok_or_else(|| anyhow::anyhow!("Failed to get inserted id"))
    }

    pub async fn find_by_user(&self, user_id: &ObjectId) -> Result<Vec<Session>> {
        let cursor = self
            .collection
            .find(
                doc! {
                    "user_id": user_id,
                    "is_revoked": false,
                    "expires_at": { "$gt": mongodb::bson::DateTime::now() }
                },
                None,
            )
            .await?;

        let sessions: Vec<Session> = cursor.try_collect().await?;
        Ok(sessions)
    }

    pub async fn revoke(&self, session_id: &ObjectId) -> Result<()> {
        self.collection
            .update_one(
                doc! { "_id": session_id },
                doc! { "$set": { "is_revoked": true } },
                None,
            )
            .await?;
        Ok(())
    }

    pub async fn revoke_all_for_user(&self, user_id: &ObjectId) -> Result<()> {
        self.collection
            .update_many(
                doc! {
                    "user_id": user_id,
                    "is_revoked": false
                },
                doc! { "$set": { "is_revoked": true } },
                None,
            )
            .await?;
        Ok(())
    }

    pub async fn cleanup_expired(&self) -> Result<u64> {
        let result = self
            .collection
            .delete_many(
                doc! {
                    "expires_at": { "$lt": mongodb::bson::DateTime::now() }
                },
                None,
            )
            .await?;
        Ok(result.deleted_count)
    }
}
