use axum::{
    extract::State,
    http::{HeaderMap, StatusCode},
    response::IntoResponse,
    Json,
};
use std::sync::Arc;

use crate::{
    db::PreferencesRepository,
    models::{ApiError, UpdatePreferencesRequest, UserPreferencesResponse},
    AppState,
};

/// Get user preferences
pub async fn get_preferences(
    State(state): State<Arc<AppState>>,
    headers: HeaderMap,
) -> Result<impl IntoResponse, ApiError> {
    // Extract session token from headers
    let session_token = headers
        .get("X-Session-Token")
        .and_then(|h| h.to_str().ok())
        .ok_or_else(|| ApiError::Unauthorized("Missing session token".to_string()))?;

    // Validate session and get user ID
    let user_id = state.auth_service.validate_session(session_token).await?;

    // Get preferences from database
    let prefs_repo = PreferencesRepository::new(&state.db);
    let preferences = prefs_repo
        .get_or_create(&user_id)
        .await
        .map_err(|e| ApiError::Database(format!("Failed to get preferences: {}", e)))?;

    Ok(Json(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,
    }))
}

/// Update user preferences
pub async fn update_preferences(
    State(state): State<Arc<AppState>>,
    headers: HeaderMap,
    Json(request): Json<UpdatePreferencesRequest>,
) -> Result<impl IntoResponse, ApiError> {
    // Extract session token from headers
    let session_token = headers
        .get("X-Session-Token")
        .and_then(|h| h.to_str().ok())
        .ok_or_else(|| ApiError::Unauthorized("Missing session token".to_string()))?;

    // Validate session and get user ID
    let user_id = state.auth_service.validate_session(session_token).await?;

    // Update preferences in database
    let prefs_repo = PreferencesRepository::new(&state.db);
    
    let mut preferences = prefs_repo
        .get_or_create(&user_id)
        .await
        .map_err(|e| ApiError::Database(format!("Failed to get preferences: {}", e)))?;

    // Update only provided fields
    if request.calendar_selected_date.is_some() {
        preferences.calendar_selected_date = request.calendar_selected_date;
    }
    if request.calendar_time_increment.is_some() {
        preferences.calendar_time_increment = request.calendar_time_increment;
    }
    if request.calendar_view_mode.is_some() {
        preferences.calendar_view_mode = request.calendar_view_mode;
    }
    if request.calendar_theme.is_some() {
        preferences.calendar_theme = request.calendar_theme;
    }
    if request.calendar_style.is_some() {
        preferences.calendar_style = request.calendar_style;
    }
    if request.calendar_colors.is_some() {
        preferences.calendar_colors = request.calendar_colors;
    }
    if request.last_used_calendar.is_some() {
        preferences.last_used_calendar = request.last_used_calendar;
    }

    prefs_repo
        .update(&preferences)
        .await
        .map_err(|e| ApiError::Database(format!("Failed to update preferences: {}", e)))?;

    Ok((
        StatusCode::OK,
        Json(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,
        }),
    ))
}

/// Logout user
pub async fn logout(
    State(state): State<Arc<AppState>>,
    headers: HeaderMap,
) -> Result<impl IntoResponse, ApiError> {
    // Extract session token from headers
    let session_token = headers
        .get("X-Session-Token")
        .and_then(|h| h.to_str().ok())
        .ok_or_else(|| ApiError::Unauthorized("Missing session token".to_string()))?;

    // Delete session
    state.auth_service.logout(session_token).await?;

    Ok((
        StatusCode::OK,
        Json(serde_json::json!({
            "success": true,
            "message": "Logged out successfully"
        })),
    ))
}