use chrono::{Duration, Utc};
use jsonwebtoken::{decode, encode, Algorithm, DecodingKey, EncodingKey, Header, Validation};
use serde::{Deserialize, Serialize};
use uuid::Uuid;

use crate::calendar::CalDAVClient;
use crate::config::CalDAVConfig;
use crate::db::{Database, PreferencesRepository, Session, SessionRepository, UserRepository};
use crate::models::{ApiError, AuthResponse, CalDAVLoginRequest, UserPreferencesResponse};

#[derive(Debug, Serialize, Deserialize)]
pub struct Claims {
    pub username: String,
    pub server_url: String,
    pub exp: i64, // Expiration time
    pub iat: i64, // Issued at
}

#[derive(Clone)]
pub struct AuthService {
    jwt_secret: String,
    db: Database,
}

impl AuthService {
    pub fn new(jwt_secret: String, db: Database) -> Self {
        Self { jwt_secret, db }
    }

    /// Authenticate user directly against CalDAV server
    pub async fn login(&self, request: CalDAVLoginRequest) -> Result<AuthResponse, ApiError> {
        // Validate input
        self.validate_login(&request)?;
        println!("✅ Input validation passed");

        // Create CalDAV config with provided credentials
        let caldav_config = CalDAVConfig::new(
            request.server_url.clone(),
            request.username.clone(),
            request.password.clone(),
        );

        // Test authentication against CalDAV server
        let caldav_client = CalDAVClient::new(caldav_config.clone());

        // Try to discover calendars as an authentication test
        match caldav_client.discover_calendars().await {
            Ok(_calendars) => {
                
                // Find or create user in database
                let user_repo = UserRepository::new(&self.db);
                let user = user_repo
                    .find_or_create(&request.username, &request.server_url)
                    .await
                    .map_err(|e| ApiError::Database(format!("Failed to create user: {}", e)))?;
                
                // Generate JWT token
                let jwt_token = self.generate_token(&request.username, &request.server_url)?;
                
                // Generate session token
                let session_token = format!("sess_{}", Uuid::new_v4());
                
                // Create session in database
                let session = Session::new(user.id.clone(), session_token.clone(), 24);
                let session_repo = SessionRepository::new(&self.db);
                session_repo
                    .create(&session)
                    .await
                    .map_err(|e| ApiError::Database(format!("Failed to create session: {}", e)))?;
                
                // Get or create user preferences
                let prefs_repo = PreferencesRepository::new(&self.db);
                let preferences = prefs_repo
                    .get_or_create(&user.id)
                    .await
                    .map_err(|e| ApiError::Database(format!("Failed to get preferences: {}", e)))?;
                
                Ok(AuthResponse {
                    token: jwt_token,
                    session_token,
                    username: request.username,
                    server_url: request.server_url,
                    preferences: UserPreferencesResponse {
                        calendar_selected_date: preferences.calendar_selected_date,
                        calendar_time_increment: preferences.calendar_time_increment,
                        calendar_view_mode: preferences.calendar_view_mode,
                        calendar_theme: preferences.calendar_theme,
                        calendar_style: preferences.calendar_style,
                        calendar_colors: preferences.calendar_colors,
                        last_used_calendar: preferences.last_used_calendar,
                    },
                })
            }
            Err(err) => {
                println!("❌ Authentication failed: {:?}", err);
                // Authentication failed
                Err(ApiError::Unauthorized(
                    "Invalid CalDAV credentials or server unavailable".to_string(),
                ))
            }
        }
    }

    /// Verify JWT token and extract CalDAV credentials info
    pub fn verify_token(&self, token: &str) -> Result<Claims, ApiError> {
        self.decode_token(token)
    }

    /// Get user from token
    pub async fn get_user_from_token(&self, token: &str) -> Result<crate::db::User, ApiError> {
        let claims = self.verify_token(token)?;
        
        let user_repo = UserRepository::new(&self.db);
        user_repo
            .find_or_create(&claims.username, &claims.server_url)
            .await
            .map_err(|e| ApiError::Database(format!("Failed to get user: {}", e)))
    }

    /// Create CalDAV config from token
    pub fn caldav_config_from_token(
        &self,
        token: &str,
        password: &str,
    ) -> Result<CalDAVConfig, ApiError> {
        let claims = self.verify_token(token)?;

        Ok(CalDAVConfig::new(
            claims.server_url,
            claims.username,
            password.to_string(),
        ))
    }

    fn validate_login(&self, request: &CalDAVLoginRequest) -> Result<(), ApiError> {
        if request.username.trim().is_empty() {
            return Err(ApiError::BadRequest("Username is required".to_string()));
        }

        if request.password.trim().is_empty() {
            return Err(ApiError::BadRequest("Password is required".to_string()));
        }

        if request.server_url.trim().is_empty() {
            return Err(ApiError::BadRequest("Server URL is required".to_string()));
        }

        // Basic URL validation
        if !request.server_url.starts_with("http://") && !request.server_url.starts_with("https://")
        {
            return Err(ApiError::BadRequest(
                "Server URL must start with http:// or https://".to_string(),
            ));
        }

        Ok(())
    }

    pub fn generate_token(&self, username: &str, server_url: &str) -> Result<String, ApiError> {
        let now = Utc::now();
        let expires_at = now + Duration::hours(24); // Token valid for 24 hours

        let claims = Claims {
            username: username.to_string(),
            server_url: server_url.to_string(),
            exp: expires_at.timestamp(),
            iat: now.timestamp(),
        };

        let token = encode(
            &Header::default(),
            &claims,
            &EncodingKey::from_secret(self.jwt_secret.as_bytes()),
        )
        .map_err(|e| ApiError::Internal(format!("Token generation failed: {}", e)))?;

        Ok(token)
    }

    fn decode_token(&self, token: &str) -> Result<Claims, ApiError> {
        let token_data = decode::<Claims>(
            token,
            &DecodingKey::from_secret(self.jwt_secret.as_bytes()),
            &Validation::new(Algorithm::HS256),
        )
        .map_err(|_| ApiError::Unauthorized("Invalid token".to_string()))?;

        Ok(token_data.claims)
    }
    
    /// Validate session token
    pub async fn validate_session(&self, session_token: &str) -> Result<String, ApiError> {
        let session_repo = SessionRepository::new(&self.db);
        
        let session = session_repo
            .find_by_token(session_token)
            .await
            .map_err(|e| ApiError::Database(format!("Failed to find session: {}", e)))?
            .ok_or_else(|| ApiError::Unauthorized("Invalid session token".to_string()))?;
        
        if session.is_expired() {
            return Err(ApiError::Unauthorized("Session expired".to_string()));
        }
        
        Ok(session.user_id)
    }
    
    /// Logout user by deleting session
    pub async fn logout(&self, session_token: &str) -> Result<(), ApiError> {
        let session_repo = SessionRepository::new(&self.db);
        
        session_repo
            .delete(session_token)
            .await
            .map_err(|e| ApiError::Database(format!("Failed to delete session: {}", e)))?;
        
        Ok(())
    }
}
