use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use sqlx::sqlite::{SqlitePool, SqlitePoolOptions};
use sqlx::{FromRow, Result};
use std::sync::Arc;
use uuid::Uuid;

/// Database connection pool wrapper
#[derive(Clone)]
pub struct Database {
    pool: Arc<SqlitePool>,
}

impl Database {
    /// Create a new database connection pool
    pub async fn new(database_url: &str) -> Result<Self> {
        let pool = SqlitePoolOptions::new()
            .max_connections(5)
            .connect(database_url)
            .await?;

        // Run migrations
        sqlx::migrate!("./migrations").run(&pool).await?;

        Ok(Self {
            pool: Arc::new(pool),
        })
    }

    /// Get a reference to the connection pool
    pub fn pool(&self) -> &SqlitePool {
        &self.pool
    }
}

/// User model representing a CalDAV user
#[derive(Debug, Clone, Serialize, Deserialize, FromRow)]
pub struct User {
    pub id: String, // UUID as string for SQLite
    pub username: String,
    pub server_url: String,
    pub created_at: DateTime<Utc>,
}

impl User {
    /// Create a new user with generated UUID
    pub fn new(username: String, server_url: String) -> Self {
        Self {
            id: Uuid::new_v4().to_string(),
            username,
            server_url,
            created_at: Utc::now(),
        }
    }
}

/// Session model for user sessions
#[derive(Debug, Clone, Serialize, Deserialize, FromRow)]
pub struct Session {
    pub id: String,      // UUID as string
    pub user_id: String, // Foreign key to User
    pub token: String,   // Session token
    pub created_at: DateTime<Utc>,
    pub expires_at: DateTime<Utc>,
    pub last_accessed: DateTime<Utc>,
}

impl Session {
    /// Create a new session for a user
    pub fn new(user_id: String, token: String, expires_in_hours: i64) -> Self {
        let now = Utc::now();
        Self {
            id: Uuid::new_v4().to_string(),
            user_id,
            token,
            created_at: now,
            expires_at: now + chrono::Duration::hours(expires_in_hours),
            last_accessed: now,
        }
    }

    /// Check if the session has expired
    pub fn is_expired(&self) -> bool {
        Utc::now() > self.expires_at
    }
}

/// User preferences model
#[derive(Debug, Clone, Serialize, Deserialize, FromRow)]
pub struct UserPreferences {
    pub user_id: String,
    pub calendar_selected_date: Option<String>,
    pub calendar_time_increment: Option<i32>,
    pub calendar_view_mode: Option<String>,
    pub calendar_theme: Option<String>,
    pub calendar_style: Option<String>,
    pub calendar_colors: Option<String>, // JSON string
    pub updated_at: DateTime<Utc>,
}

impl UserPreferences {
    /// Create default preferences for a new user
    pub fn default_for_user(user_id: String) -> Self {
        Self {
            user_id,
            calendar_selected_date: None,
            calendar_time_increment: Some(15),
            calendar_view_mode: Some("month".to_string()),
            calendar_theme: Some("light".to_string()),
            calendar_style: Some("default".to_string()),
            calendar_colors: None,
            updated_at: Utc::now(),
        }
    }
}

/// Repository for User operations
pub struct UserRepository<'a> {
    db: &'a Database,
}

impl<'a> UserRepository<'a> {
    pub fn new(db: &'a Database) -> Self {
        Self { db }
    }

    /// Find or create a user by username and server URL
    pub async fn find_or_create(
        &self,
        username: &str,
        server_url: &str,
    ) -> Result<User> {
        // Try to find existing user
        let existing = sqlx::query_as::<_, User>(
            "SELECT * FROM users WHERE username = ? AND server_url = ?",
        )
        .bind(username)
        .bind(server_url)
        .fetch_optional(self.db.pool())
        .await?;

        if let Some(user) = existing {
            Ok(user)
        } else {
            // Create new user
            let user = User::new(username.to_string(), server_url.to_string());
            
            sqlx::query(
                "INSERT INTO users (id, username, server_url, created_at) VALUES (?, ?, ?, ?)",
            )
            .bind(&user.id)
            .bind(&user.username)
            .bind(&user.server_url)
            .bind(&user.created_at)
            .execute(self.db.pool())
            .await?;

            Ok(user)
        }
    }

    /// Find a user by ID
    pub async fn find_by_id(&self, user_id: &str) -> Result<Option<User>> {
        sqlx::query_as::<_, User>("SELECT * FROM users WHERE id = ?")
            .bind(user_id)
            .fetch_optional(self.db.pool())
            .await
    }
}

/// Repository for Session operations
pub struct SessionRepository<'a> {
    db: &'a Database,
}

impl<'a> SessionRepository<'a> {
    pub fn new(db: &'a Database) -> Self {
        Self { db }
    }

    /// Create a new session
    pub async fn create(&self, session: &Session) -> Result<()> {
        sqlx::query(
            "INSERT INTO sessions (id, user_id, token, created_at, expires_at, last_accessed) 
             VALUES (?, ?, ?, ?, ?, ?)",
        )
        .bind(&session.id)
        .bind(&session.user_id)
        .bind(&session.token)
        .bind(&session.created_at)
        .bind(&session.expires_at)
        .bind(&session.last_accessed)
        .execute(self.db.pool())
        .await?;

        Ok(())
    }

    /// Find a session by token and update last_accessed
    pub async fn find_by_token(&self, token: &str) -> Result<Option<Session>> {
        let session = sqlx::query_as::<_, Session>("SELECT * FROM sessions WHERE token = ?")
            .bind(token)
            .fetch_optional(self.db.pool())
            .await?;

        if let Some(ref s) = session {
            if !s.is_expired() {
                // Update last_accessed time
                sqlx::query("UPDATE sessions SET last_accessed = ? WHERE id = ?")
                    .bind(Utc::now())
                    .bind(&s.id)
                    .execute(self.db.pool())
                    .await?;
            }
        }

        Ok(session)
    }

    /// Delete a session (logout)
    pub async fn delete(&self, token: &str) -> Result<()> {
        sqlx::query("DELETE FROM sessions WHERE token = ?")
            .bind(token)
            .execute(self.db.pool())
            .await?;

        Ok(())
    }

    /// Clean up expired sessions
    pub async fn cleanup_expired(&self) -> Result<u64> {
        let result = sqlx::query("DELETE FROM sessions WHERE expires_at < ?")
            .bind(Utc::now())
            .execute(self.db.pool())
            .await?;

        Ok(result.rows_affected())
    }
}

/// Repository for UserPreferences operations
pub struct PreferencesRepository<'a> {
    db: &'a Database,
}

impl<'a> PreferencesRepository<'a> {
    pub fn new(db: &'a Database) -> Self {
        Self { db }
    }

    /// Get user preferences, creating defaults if not exist
    pub async fn get_or_create(&self, user_id: &str) -> Result<UserPreferences> {
        let existing = sqlx::query_as::<_, UserPreferences>(
            "SELECT * FROM user_preferences WHERE user_id = ?",
        )
        .bind(user_id)
        .fetch_optional(self.db.pool())
        .await?;

        if let Some(prefs) = existing {
            Ok(prefs)
        } else {
            // Create default preferences
            let prefs = UserPreferences::default_for_user(user_id.to_string());
            
            sqlx::query(
                "INSERT INTO user_preferences 
                 (user_id, calendar_selected_date, calendar_time_increment, 
                  calendar_view_mode, calendar_theme, calendar_style, calendar_colors, updated_at) 
                 VALUES (?, ?, ?, ?, ?, ?, ?, ?)",
            )
            .bind(&prefs.user_id)
            .bind(&prefs.calendar_selected_date)
            .bind(&prefs.calendar_time_increment)
            .bind(&prefs.calendar_view_mode)
            .bind(&prefs.calendar_theme)
            .bind(&prefs.calendar_style)
            .bind(&prefs.calendar_colors)
            .bind(&prefs.updated_at)
            .execute(self.db.pool())
            .await?;

            Ok(prefs)
        }
    }

    /// Update user preferences
    pub async fn update(&self, prefs: &UserPreferences) -> Result<()> {
        sqlx::query(
            "UPDATE user_preferences 
             SET calendar_selected_date = ?, calendar_time_increment = ?, 
                 calendar_view_mode = ?, calendar_theme = ?, calendar_style = ?,
                 calendar_colors = ?, updated_at = ?
             WHERE user_id = ?",
        )
        .bind(&prefs.calendar_selected_date)
        .bind(&prefs.calendar_time_increment)
        .bind(&prefs.calendar_view_mode)
        .bind(&prefs.calendar_theme)
        .bind(&prefs.calendar_style)
        .bind(&prefs.calendar_colors)
        .bind(Utc::now())
        .bind(&prefs.user_id)
        .execute(self.db.pool())
        .await?;

        Ok(())
    }
}