#![allow(dead_code)]
#![allow(unused_imports)]
#![allow(clippy::redundant_closure)]
#![allow(clippy::useless_conversion)]
use mongodb::bson::DateTime;
use mongodb::bson::{doc, oid::ObjectId};
use mongodb::Collection;
use serde::{Deserialize, Serialize};

use super::permission::Permission;

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Share {
    #[serde(rename = "_id", skip_serializing_if = "Option::is_none")]
    pub id: Option<ObjectId>,
    pub owner_id: ObjectId,
    pub target_user_id: ObjectId,
    pub resource_type: String,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub resource_id: Option<ObjectId>,
    pub permissions: Vec<Permission>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub expires_at: Option<DateTime>,
    pub created_at: DateTime,
    pub active: bool,
}

impl Share {
    pub fn new(
        owner_id: ObjectId,
        target_user_id: ObjectId,
        resource_type: String,
        resource_id: Option<ObjectId>,
        permissions: Vec<Permission>,
        expires_at: Option<DateTime>,
    ) -> Self {
        Self {
            id: None,
            owner_id,
            target_user_id,
            resource_type,
            resource_id,
            permissions,
            expires_at,
            created_at: DateTime::now(),
            active: true,
        }
    }

    pub fn is_expired(&self) -> bool {
        if let Some(expires) = self.expires_at {
            DateTime::now() > expires
        } else {
            false
        }
    }

    pub fn has_permission(&self, permission: &Permission) -> bool {
        self.permissions.contains(permission) || self.permissions.contains(&Permission::Admin)
    }

    pub fn revoke(&mut self) {
        self.active = false;
    }
}

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

impl ShareRepository {
    pub fn new(collection: Collection<Share>) -> Self {
        Self { collection }
    }

    pub async fn create(&self, share: &Share) -> mongodb::error::Result<Option<ObjectId>> {
        let result = self.collection.insert_one(share, None).await?;
        Ok(Some(result.inserted_id.as_object_id().unwrap()))
    }

    pub async fn find_by_id(&self, id: &ObjectId) -> mongodb::error::Result<Option<Share>> {
        self.collection.find_one(doc! { "_id": id }, None).await
    }

    pub async fn find_by_owner(&self, owner_id: &ObjectId) -> mongodb::error::Result<Vec<Share>> {
        use futures::stream::TryStreamExt;

        self.collection
            .find(doc! { "owner_id": owner_id }, None)
            .await?
            .try_collect()
            .await
            .map_err(|e| mongodb::error::Error::from(e))
    }

    pub async fn find_by_target(
        &self,
        target_user_id: &ObjectId,
    ) -> mongodb::error::Result<Vec<Share>> {
        use futures::stream::TryStreamExt;

        self.collection
            .find(
                doc! { "target_user_id": target_user_id, "active": true },
                None,
            )
            .await?
            .try_collect()
            .await
            .map_err(|e| mongodb::error::Error::from(e))
    }

    pub async fn update(&self, share: &Share) -> mongodb::error::Result<()> {
        if let Some(id) = &share.id {
            self.collection
                .replace_one(doc! { "_id": id }, share, None)
                .await?;
        }
        Ok(())
    }

    pub async fn delete(&self, share_id: &ObjectId) -> mongodb::error::Result<()> {
        self.collection
            .delete_one(doc! { "_id": share_id }, None)
            .await?;
        Ok(())
    }
}
