//! User billing and quota management

use chrono::{DateTime, Utc};
use std::sync::Mutex;
use thiserror::Error;
use uuid::Uuid;

#[derive(Error, Debug)]
pub enum BillingError {
    #[error("user not found: {0}")]
    UserNotFound(Uuid),
    #[error("subscription expired")]
    Expired,
    #[error("bandwidth quota exceeded")]
    QuotaExceeded,
    #[error("database error: {0}")]
    Database(#[from] rusqlite::Error),
    #[error("lock error")]
    LockError,
}

/// User account with billing info
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct User {
    pub uuid: Uuid,
    pub bandwidth_quota: u64,      // bytes allowed
    pub bandwidth_used: u64,       // bytes used
    pub expires_at: DateTime<Utc>, // subscription expiry
    pub active: bool,
}

impl User {
    pub fn new(quota_gb: u64, valid_days: i64) -> Self {
        Self {
            uuid: Uuid::new_v4(),
            bandwidth_quota: quota_gb * 1024 * 1024 * 1024,
            bandwidth_used: 0,
            expires_at: Utc::now() + chrono::Duration::days(valid_days),
            active: true,
        }
    }

    pub fn is_valid(&self) -> bool {
        self.active && Utc::now() < self.expires_at && self.bandwidth_used < self.bandwidth_quota
    }

    pub fn remaining_quota(&self) -> u64 {
        self.bandwidth_quota.saturating_sub(self.bandwidth_used)
    }
}

/// Abstract user registry trait
pub trait UserRegistry: Send + Sync {
    fn get_user(&self, uuid: &Uuid) -> Result<Option<User>, BillingError>;
    fn create_user(&self, user: &User) -> Result<(), BillingError>;
    fn update_bandwidth(&self, uuid: &Uuid, bytes: u64) -> Result<(), BillingError>;
    fn validate_user(&self, uuid: &Uuid) -> Result<bool, BillingError>;
}

/// SQLite implementation (thread-safe via Mutex)
pub struct SqliteRegistry {
    pub conn: Mutex<rusqlite::Connection>,
}

impl SqliteRegistry {
    pub fn new(path: &str) -> Result<Self, BillingError> {
        let conn = rusqlite::Connection::open(path)?;
        conn.execute(
            "CREATE TABLE IF NOT EXISTS users (
                uuid TEXT PRIMARY KEY,
                bandwidth_quota INTEGER NOT NULL,
                bandwidth_used INTEGER NOT NULL DEFAULT 0,
                expires_at TEXT NOT NULL,
                active INTEGER NOT NULL DEFAULT 1
            )",
            [],
        )?;
        Ok(Self { conn: Mutex::new(conn) })
    }

    pub fn in_memory() -> Result<Self, BillingError> {
        Self::new(":memory:")
    }
}

impl UserRegistry for SqliteRegistry {
    fn get_user(&self, uuid: &Uuid) -> Result<Option<User>, BillingError> {
        let conn = self.conn.lock().map_err(|_| BillingError::LockError)?;
        let mut stmt = conn
            .prepare("SELECT uuid, bandwidth_quota, bandwidth_used, expires_at, active FROM users WHERE uuid = ?")?;

        let mut rows = stmt.query([uuid.to_string()])?;

        if let Some(row) = rows.next()? {
            let uuid_str: String = row.get(0)?;
            let expires_str: String = row.get(3)?;
            Ok(Some(User {
                uuid: Uuid::parse_str(&uuid_str).unwrap(),
                bandwidth_quota: row.get(1)?,
                bandwidth_used: row.get(2)?,
                expires_at: DateTime::parse_from_rfc3339(&expires_str)
                    .unwrap()
                    .with_timezone(&Utc),
                active: row.get::<_, i32>(4)? == 1,
            }))
        } else {
            Ok(None)
        }
    }

    fn create_user(&self, user: &User) -> Result<(), BillingError> {
        let conn = self.conn.lock().map_err(|_| BillingError::LockError)?;
        conn.execute(
            "INSERT INTO users (uuid, bandwidth_quota, bandwidth_used, expires_at, active) VALUES (?, ?, ?, ?, ?)",
            (
                user.uuid.to_string(),
                user.bandwidth_quota,
                user.bandwidth_used,
                user.expires_at.to_rfc3339(),
                if user.active { 1 } else { 0 },
            ),
        )?;
        Ok(())
    }

    fn update_bandwidth(&self, uuid: &Uuid, bytes: u64) -> Result<(), BillingError> {
        let conn = self.conn.lock().map_err(|_| BillingError::LockError)?;
        let updated = conn.execute(
            "UPDATE users SET bandwidth_used = bandwidth_used + ? WHERE uuid = ?",
            (bytes, uuid.to_string()),
        )?;
        if updated == 0 {
            return Err(BillingError::UserNotFound(*uuid));
        }
        Ok(())
    }

    fn validate_user(&self, uuid: &Uuid) -> Result<bool, BillingError> {
        match self.get_user(uuid)? {
            Some(user) => Ok(user.is_valid()),
            None => Ok(false),
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_user_lifecycle() {
        let reg = SqliteRegistry::in_memory().unwrap();
        let user = User::new(100, 30); // 100GB, 30 days
        let uuid = user.uuid;

        reg.create_user(&user).unwrap();
        assert!(reg.validate_user(&uuid).unwrap());

        reg.update_bandwidth(&uuid, 1024).unwrap();
        let updated = reg.get_user(&uuid).unwrap().unwrap();
        assert_eq!(updated.bandwidth_used, 1024);
    }
}
