use gloo_storage::{LocalStorage, Storage};
use serde::{Deserialize, Serialize};
use serde_json;
use wasm_bindgen::JsCast;
use wasm_bindgen_futures::JsFuture;
use web_sys::{Request, RequestInit, RequestMode, Response};

#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct UserPreferences {
    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_colors: Option<String>,
    pub last_used_calendar: Option<String>,
}

#[derive(Debug, Serialize)]
#[allow(dead_code)]
pub struct UpdatePreferencesRequest {
    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_colors: Option<String>,
    pub last_used_calendar: Option<String>,
}

#[allow(dead_code)]
pub struct PreferencesService {
    base_url: String,
}

#[allow(dead_code)]
impl PreferencesService {
    pub fn new() -> Self {
        let base_url = option_env!("BACKEND_API_URL")
            .unwrap_or("http://localhost:3000/api")
            .to_string();
        
        Self { base_url }
    }
    
    /// Load preferences from LocalStorage (cached from login)
    pub fn load_cached() -> Option<UserPreferences> {
        if let Ok(prefs_json) = LocalStorage::get::<String>("user_preferences") {
            serde_json::from_str(&prefs_json).ok()
        } else {
            None
        }
    }
    
    /// Update a single preference field and sync with backend
    pub async fn update_preference(&self, field: &str, value: serde_json::Value) -> Result<(), String> {
        // Get session token
        let session_token = LocalStorage::get::<String>("session_token")
            .map_err(|_| "No session token found".to_string())?;
        
        // Load current preferences
        let mut preferences = Self::load_cached().unwrap_or(UserPreferences {
            calendar_selected_date: None,
            calendar_time_increment: None,
            calendar_view_mode: None,
            calendar_theme: None,
            calendar_colors: None,
            last_used_calendar: None,
        });
        
        // Update the specific field
        match field {
            "calendar_selected_date" => {
                preferences.calendar_selected_date = value.as_str().map(|s| s.to_string());
            }
            "calendar_time_increment" => {
                preferences.calendar_time_increment = value.as_i64().map(|i| i as i32);
            }
            "calendar_view_mode" => {
                preferences.calendar_view_mode = value.as_str().map(|s| s.to_string());
            }
            "calendar_theme" => {
                preferences.calendar_theme = value.as_str().map(|s| s.to_string());
            }
            "calendar_colors" => {
                preferences.calendar_colors = value.as_str().map(|s| s.to_string());
            }
            _ => return Err(format!("Unknown preference field: {}", field)),
        }
        
        // Save to LocalStorage cache
        if let Ok(prefs_json) = serde_json::to_string(&preferences) {
            let _ = LocalStorage::set("user_preferences", &prefs_json);
        }
        
        // Sync with backend
        let request = UpdatePreferencesRequest {
            calendar_selected_date: preferences.calendar_selected_date.clone(),
            calendar_time_increment: preferences.calendar_time_increment,
            calendar_view_mode: preferences.calendar_view_mode.clone(),
            calendar_theme: preferences.calendar_theme.clone(),
            calendar_colors: preferences.calendar_colors.clone(),
            last_used_calendar: preferences.last_used_calendar.clone(),
        };
        
        self.sync_preferences(&session_token, &request).await
    }
    
    /// Sync all preferences with backend
    async fn sync_preferences(
        &self,
        session_token: &str,
        request: &UpdatePreferencesRequest,
    ) -> Result<(), String> {
        let window = web_sys::window().ok_or("No global window exists")?;
        
        let json_body = serde_json::to_string(request)
            .map_err(|e| format!("JSON serialization failed: {}", e))?;
        
        let opts = RequestInit::new();
        opts.set_method("POST");
        opts.set_mode(RequestMode::Cors);
        opts.set_body(&wasm_bindgen::JsValue::from_str(&json_body));
        
        let url = format!("{}/preferences", self.base_url);
        let request = Request::new_with_str_and_init(&url, &opts)
            .map_err(|e| format!("Request creation failed: {:?}", e))?;
        
        request
            .headers()
            .set("Content-Type", "application/json")
            .map_err(|e| format!("Header setting failed: {:?}", e))?;
        
        request
            .headers()
            .set("X-Session-Token", session_token)
            .map_err(|e| format!("Header setting failed: {:?}", e))?;
        
        let resp_value = JsFuture::from(window.fetch_with_request(&request))
            .await
            .map_err(|e| format!("Network request failed: {:?}", e))?;
        
        let resp: Response = resp_value
            .dyn_into()
            .map_err(|e| format!("Response cast failed: {:?}", e))?;
        
        if resp.ok() {
            Ok(())
        } else {
            Err(format!("Failed to update preferences: {}", resp.status()))
        }
    }
    
    /// Migrate preferences from LocalStorage to backend (on first login after update)
    pub async fn migrate_from_local_storage(&self) -> Result<(), String> {
        let session_token = LocalStorage::get::<String>("session_token")
            .map_err(|_| "No session token found".to_string())?;
        
        let request = UpdatePreferencesRequest {
            calendar_selected_date: LocalStorage::get::<String>("calendar_selected_date").ok(),
            calendar_time_increment: LocalStorage::get::<u32>("calendar_time_increment").ok().map(|i| i as i32),
            calendar_view_mode: LocalStorage::get::<String>("calendar_view_mode").ok(),
            calendar_theme: LocalStorage::get::<String>("calendar_theme").ok(),
            calendar_colors: LocalStorage::get::<String>("calendar_colors").ok(),
            last_used_calendar: LocalStorage::get::<String>("last_used_calendar").ok(),
        };
        
        // Only migrate if we have some preferences to migrate
        if request.calendar_selected_date.is_some()
            || request.calendar_time_increment.is_some()
            || request.calendar_view_mode.is_some()
            || request.calendar_theme.is_some()
            || request.calendar_colors.is_some()
            || request.last_used_calendar.is_some()
        {
            self.sync_preferences(&session_token, &request).await?;
            
            // Clear old LocalStorage entries after successful migration
            let _ = LocalStorage::delete("calendar_selected_date");
            let _ = LocalStorage::delete("calendar_time_increment");
            let _ = LocalStorage::delete("calendar_view_mode");
            let _ = LocalStorage::delete("calendar_theme");
            let _ = LocalStorage::delete("calendar_colors");
        }
        
        Ok(())
    }

    /// Update the last used calendar and sync with backend
    pub async fn update_last_used_calendar(&self, calendar_path: &str) -> Result<(), String> {
        // Get session token
        let session_token = LocalStorage::get::<String>("session_token")
            .map_err(|_| "No session token found".to_string())?;
        
        // Create minimal update request with only the last used calendar
        let request = UpdatePreferencesRequest {
            calendar_selected_date: None,
            calendar_time_increment: None,
            calendar_view_mode: None,
            calendar_theme: None,
            calendar_colors: None,
            last_used_calendar: Some(calendar_path.to_string()),
        };
        
        // Sync to backend
        self.sync_preferences(&session_token, &request).await
    }
}