use crate::model::auth::AccessToken;
use crate::model::user::{User, UserRole};
use chrono::{DateTime, Utc};
use crate::api::totp::TotpStatus;
use crate::model::space::Space;

pub mod user_repo;
pub mod message_repo;
pub mod access_token_repo;
pub mod space_repo;
pub mod channel_repo;
pub mod mock;

pub trait Repo: Clone + Send + Sync {
	type Target;

	fn new(pool: sqlx::PgPool) -> Self;

	async fn get_by_id(&self, id: i64) -> Option<Self::Target>;
}

#[rocket::async_trait]
pub trait UserRepo: Send + Sync {
	fn pool(&self) -> &sqlx::PgPool;
	async fn get_by_id(&self, id: i64) -> Option<User>;
	async fn save(&self, user: &User) -> Result<(), sqlx::Error>;
	async fn new_user(&self, email: &str, username: &str, pass_hash: &str) -> Result<i64, sqlx::Error>;

	async fn is_admin(&self, uid: i64) -> Result<UserRole, sqlx::Error>;
	async fn get_by_username(&self, username: &str) -> Result<Option<User>, sqlx::Error>;
	async fn delete_by_id(&self, id: i64) -> Result<(), sqlx::Error>;
	async fn set_display_name(&self, id: i64, display_name: Option<String>) -> Result<(), sqlx::Error>;
	async fn set_username(&self, id: i64, username: &str) -> Result<(), sqlx::Error>;
	async fn set_twofa_enabled(&self, id: i64, enabled: &TotpStatus) -> Result<(), sqlx::Error>;
	async fn get_totp_secret(&self, id: i64) -> Result<Option<String>, sqlx::Error>;
	async fn set_totp_secret(&self, id: i64, secret: Option<String>) -> Result<(), sqlx::Error>;
	async fn get_pass_hash(&self, id: i64) -> Result<String, sqlx::Error>;
	async fn set_pass_hash(&self, id: i64, pass_hash: &str) -> Result<(), sqlx::Error>;
	async fn set_email(&self, id: i64, email: &str) -> Result<(), sqlx::Error>;
	async fn set_role(&self, id: i64, role: &str) -> Result<(), sqlx::Error>;
}

#[rocket::async_trait]
pub trait SpaceRepo: Send + Sync {
	async fn create(&self, name: &str, description: Option<&str>, owner_id: i64) -> Result<i64, sqlx::Error>;
	async fn get_all(&self) -> Result<Vec<crate::model::space::Space>, sqlx::Error>;
	async fn get_by_member(&self, uid: i64) -> Result<Vec<Space>, sqlx::Error>;
	async fn get_by_id(&self, id: i64) -> Result<Option<crate::model::space::Space>, sqlx::Error>;
}

#[rocket::async_trait]
pub trait ChannelRepo: Send + Sync {
	async fn create(&self, name: &str, description: Option<&str>, space_id: i64) -> Result<i64, sqlx::Error>;
	async fn get_by_space_id(&self, space_id: i64) -> Result<Vec<crate::model::space::Channel>, sqlx::Error>;
}

#[async_trait]
pub trait AccessTokenRepoTrait: Send + Sync {
	async fn get_by_id(&self, id: i64) -> Option<AccessToken>;
	async fn create_new(&self,
		uid: i64, name: &str, code: &str, max_uses: i32,
		start_date: DateTime<Utc>, expiry_date: DateTime<Utc>
	) -> Result<i64, sqlx::Error>;
	async fn use_token(&self, id: i64) -> Result<(), sqlx::Error>;
	async fn get_code_not_expired(&self, code: &str) -> Result<Option<AccessToken>, sqlx::Error>;
}