use actix_web::{post, web, HttpResponse};
use bcrypt::{hash, verify};
use futures_util::stream::TryStreamExt; // Import TryStreamExt
use mongodb::bson::doc;
use serde::{Deserialize, Serialize};
use log::{info, error};

use crate::models::{User as ModelUser, Counter};
use crate::utils::db::MongoRepo;

#[derive(Debug, Serialize, Deserialize)]
pub struct User {
    pub id: Option<String>,
    pub name: String,
    pub email: String,
    pub password: String,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct AuthRequest {
    pub email: String,
    pub password: String,
}

async fn get_next_id(db: &MongoRepo) -> i32 {
    let counter_collection = db.collection::<Counter>("counters");
    let filter = doc! { "id": "user_id" };

    // Check if counter exists
    let counter = counter_collection
        .find_one(filter.clone())
        .await
        .expect("Failed to check counter");

    match counter {
        Some(_) => {
            // Increment existing counter
            let update = doc! { "$inc": { "seq": 1 } };
            let updated = counter_collection
                .find_one_and_update(filter, update)
                .await
                .expect("Failed to update counter")
                .unwrap();
            updated.seq
        }
        None => {
            // Initialize counter with 100
            let initial_counter = Counter {
                id: "user_id".to_string(),
                seq: 101,
            };
            counter_collection
                .insert_one(initial_counter)
                .await
                .expect("Failed to initialize counter");
            
            // Immediately increment to 101 for first user
            let update = doc! { "$inc": { "seq": 1 } };
            let updated = counter_collection
                .find_one_and_update(filter, update)
                .await
                .expect("Failed to update counter")
                .unwrap();
            updated.seq // Will return 101 for first user
        }
    }
}

#[post("/api/users/register")]
async fn register(
    db: web::Data<MongoRepo>,
    user: web::Json<User>,
) -> HttpResponse {
    info!("Received registration request for email: {}", user.email);

    let hashed_name = match hash(&user.name, 4) {
        Ok(hn) => hn,
        Err(e) => {
            error!("Error hashing name: {:?}", e);
            return HttpResponse::InternalServerError().json("Error hashing name");
        }
    };

    let hashed_email = match hash(&user.email, 4) {
        Ok(he) => he,
        Err(e) => {
            error!("Error hashing email: {:?}", e);
            return HttpResponse::InternalServerError().json("Error hashing email");
        }
    };

    let hashed_password = match hash(&user.password, 4) {
        Ok(hp) => hp,
        Err(e) => {
            error!("Error hashing password: {:?}", e);
            return HttpResponse::InternalServerError().json("Error hashing password");
        }
    };

    let user_id = get_next_id(&db).await;

    let new_user = ModelUser {
        id: Some(user_id.to_string()), // Convert user_id to String
        name: hashed_name,
        email: hashed_email,
        password: hashed_password,
    };

    let user_collection = db.collection::<ModelUser>("users");
    let result = user_collection
        .insert_one(new_user)
        .await;

    match result {
        Ok(_) => {
            info!("User registered successfully");
            HttpResponse::Ok().json("User registered successfully")
        },
        Err(e) => {
            error!("Error inserting user: {:?}", e);
            HttpResponse::InternalServerError().json("Error registering user")
        }
    }
}

#[post("/api/users/login")]
async fn login(
    db: web::Data<MongoRepo>,
    auth: web::Json<AuthRequest>,
) -> HttpResponse {
    info!("Received login request for email: {}", auth.email);

    let user_collection = db.collection::<ModelUser>("users");
    let users = user_collection
        .find(doc! {}) // Pass an empty document to find all users
        .await
        .unwrap()
        .try_collect::<Vec<ModelUser>>()
        .await
        .unwrap();

    for user in users {
        if verify(&auth.email, &user.email).unwrap() {
            if verify(&auth.password, &user.password).unwrap() {
                info!("Login successful for email: {}", auth.email);
                return HttpResponse::Ok().json("Login successful");
            } else {
                info!("Invalid credentials for email: {}", auth.email);
                return HttpResponse::Unauthorized().json("Invalid credentials");
            }
        }
    }

    info!("Invalid credentials for email: {}", auth.email);
    HttpResponse::Unauthorized().json("Invalid credentials")
}