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

use crate::auth::password::verify_password;
use mongodb::bson::DateTime;

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct User {
    #[serde(rename = "_id", skip_serializing_if = "Option::is_none")]
    pub id: Option<ObjectId>,

    pub email: String,

    pub username: String,

    pub password_hash: String,

    /// Password recovery phrase hash (zero-knowledge)
    #[serde(skip_serializing_if = "Option::is_none")]
    pub recovery_phrase_hash: Option<String>,

    /// Whether password recovery is enabled for this user
    pub recovery_enabled: bool,

    /// Token version for invalidating all tokens on password change
    pub token_version: i32,

    /// When the user was created
    pub created_at: DateTime,

    /// Last time the user was active
    pub last_active: DateTime,

    /// Email verification status
    pub email_verified: bool,

    /// Email verification token (stub for now, will be used in Phase 2.4)
    #[serde(skip_serializing_if = "Option::is_none")]
    pub verification_token: Option<String>,

    /// When the verification token expires
    #[serde(skip_serializing_if = "Option::is_none")]
    pub verification_expires: Option<DateTime>,
}

impl User {
    /// Create a new user with password and optional recovery phrase
    pub fn new(
        email: String,
        username: String,
        password: String,
        recovery_phrase: Option<String>,
    ) -> Result<Self, anyhow::Error> {
        // Import PasswordService
        use crate::auth::password::PasswordService;

        // Hash the password
        let password_hash = PasswordService::hash_password(&password)?;

        // Hash the recovery phrase if provided
        let recovery_phrase_hash = if let Some(phrase) = recovery_phrase {
            Some(PasswordService::hash_password(&phrase)?)
        } else {
            None
        };

        let now = DateTime::now();
        let recovery_enabled = recovery_phrase_hash.is_some();

        Ok(User {
            id: None,
            email,
            username,
            password_hash,
            recovery_phrase_hash,
            recovery_enabled,
            token_version: 0,
            created_at: now,
            last_active: now,
            email_verified: false,
            verification_token: None,
            verification_expires: None,
        })
    }

    /// Verify a password against the stored hash
    pub fn verify_password(&self, password: &str) -> Result<bool, anyhow::Error> {
        verify_password(password, &self.password_hash)
    }

    /// Verify a recovery phrase against the stored hash
    pub fn verify_recovery_phrase(&self, phrase: &str) -> Result<bool, anyhow::Error> {
        if !self.recovery_enabled || self.recovery_phrase_hash.is_none() {
            return Ok(false);
        }

        let hash = self.recovery_phrase_hash.as_ref().unwrap();
        verify_password(phrase, hash)
    }

    /// Update the password hash (increments token_version to invalidate all tokens)
    pub fn update_password(&mut self, new_password: String) -> Result<(), anyhow::Error> {
        use crate::auth::password::PasswordService;

        self.password_hash = PasswordService::hash_password(&new_password)?;
        self.token_version += 1;
        Ok(())
    }

    /// Set or update the recovery phrase
    pub fn set_recovery_phrase(&mut self, phrase: String) -> Result<(), anyhow::Error> {
        use crate::auth::password::PasswordService;

        self.recovery_phrase_hash = Some(PasswordService::hash_password(&phrase)?);
        self.recovery_enabled = true;
        Ok(())
    }

    /// Remove the recovery phrase (disable password recovery)
    pub fn remove_recovery_phrase(&mut self) {
        self.recovery_phrase_hash = None;
        self.recovery_enabled = false;
    }

    /// Increment token version (invalidates all existing tokens)
    pub fn increment_token_version(&mut self) {
        self.token_version += 1;
    }
}

/// Repository for User operations
#[derive(Clone)]
pub struct UserRepository {
    collection: Collection<User>,
}

impl UserRepository {
    /// Create a new UserRepository
    pub fn new(collection: Collection<User>) -> Self {
        Self { collection }
    }

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

    /// Find a user by email
    pub async fn find_by_email(&self, email: &str) -> mongodb::error::Result<Option<User>> {
        self.collection
            .find_one(doc! { "email": email }, None)
            .await
    }

    /// Find a user by ID
    pub async fn find_by_id(&self, id: &ObjectId) -> mongodb::error::Result<Option<User>> {
        self.collection.find_one(doc! { "_id": id }, None).await
    }

    /// Find a user by verification token
    pub async fn find_by_verification_token(
        &self,
        token: &str,
    ) -> mongodb::error::Result<Option<User>> {
        self.collection
            .find_one(doc! { "verification_token": token }, None)
            .await
    }

    /// Update a user
    pub async fn update(&self, user: &User) -> mongodb::error::Result<()> {
        self.collection
            .replace_one(doc! { "_id": &user.id }, user, None)
            .await?;
        Ok(())
    }

    /// Update the token version - silently fails if ObjectId is invalid
    pub async fn update_token_version(
        &self,
        user_id: &str,
        version: i32,
    ) -> mongodb::error::Result<()> {
        let oid = match ObjectId::parse_str(user_id) {
            Ok(id) => id,
            Err(_) => return Ok(()), // Silently fail if invalid ObjectId
        };

        self.collection
            .update_one(
                doc! { "_id": oid },
                doc! { "$set": { "token_version": version } },
                None,
            )
            .await?;
        Ok(())
    }

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

    /// Update last active timestamp
    pub async fn update_last_active(&self, user_id: &ObjectId) -> mongodb::error::Result<()> {
        use mongodb::bson::DateTime;

        let now = DateTime::now();
        self.collection
            .update_one(
                doc! { "_id": user_id },
                doc! { "$set": { "last_active": now } },
                None,
            )
            .await?;
        Ok(())
    }
}
