use leptos::prelude::*;
use leptos::context::Provider;
use leptos_router::hooks::use_location;
use serde::{Serialize, Deserialize};

#[cfg(target_arch = "wasm32")]
use web_sys::Storage;

#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct ReferralData {
    pub referral_code: Option<String>,  // from ?ref= or ?referry=
    pub admin_referral_code: Option<String>,  // from ?source=
}

impl Default for ReferralData {
    fn default() -> Self {
        Self {
            referral_code: None,
            admin_referral_code: None,
        }
    }
}

#[derive(Clone)]
pub struct ReferralTrackingContext {
    pub referral_data: RwSignal<ReferralData>,
    pub set_referral_code: Callback<Option<String>>,
    pub set_admin_referral_code: Callback<Option<String>>,
    pub clear_referral_data: Callback<()>,
}

impl ReferralTrackingContext {
    pub fn new() -> Self {
        let referral_data = RwSignal::new(ReferralData::default());
        
        #[cfg(target_arch = "wasm32")]
        {
            std::panic::catch_unwind(|| {
                if let Some(storage) = get_local_storage() {
                    if let Ok(Some(stored_data)) = storage.get_item("referral_data") {
                        web_sys::console::log_1(&format!("Loading referral data from localStorage: {}", stored_data).into());
                        if let Ok(parsed_data) = serde_json::from_str::<ReferralData>(&stored_data) {
                            referral_data.set(parsed_data);
                            web_sys::console::log_1(&"Referral data loaded from localStorage".into());
                        }
                    } else {
                        web_sys::console::log_1(&"No referral data found in localStorage".into());
                    }
                }
            }).unwrap_or_else(|_| {
                log::warn!("Panic occurred while loading referral data from localStorage");
            });
        }
        
        let set_referral_code = Callback::new(move |code: Option<String>| {
            referral_data.update(|data| {
                data.referral_code = code;
                #[cfg(target_arch = "wasm32")]
                {
                    if let Some(storage) = get_local_storage() {
                        if let Ok(serialized) = serde_json::to_string(data) {
                            let _ = storage.set_item("referral_data", &serialized);
                        }
                    }
                }
            });
        });
        
        let set_admin_referral_code = Callback::new(move |admin_code: Option<String>| {
            referral_data.update(|data| {
                data.admin_referral_code = admin_code;
                #[cfg(target_arch = "wasm32")]
                {
                    if let Some(storage) = get_local_storage() {
                        if let Ok(serialized) = serde_json::to_string(data) {
                            let _ = storage.set_item("referral_data", &serialized);
                        }
                    }
                }
            });
        });
        
        let clear_referral_data = Callback::new(move |_| {
            referral_data.set(ReferralData::default());
            #[cfg(target_arch = "wasm32")]
            {
                if let Some(storage) = get_local_storage() {
                    let _ = storage.remove_item("referral_data");
                }
            }
        });
        
        Self {
            referral_data,
            set_referral_code,
            set_admin_referral_code,
            clear_referral_data,
        }
    }
}

#[cfg(target_arch = "wasm32")]
fn get_local_storage() -> Option<Storage> {
    web_sys::window()?.local_storage().ok().flatten()
}

/// Hook to use referral tracking context
pub fn use_referral_tracking() -> ReferralTrackingContext {
    use_context::<ReferralTrackingContext>()
        .expect("ReferralTrackingContext should be provided")
}

/// Component to parse URL parameters and set referral data
#[component]
pub fn ReferralTracker() -> impl IntoView {
    let referral_context = use_referral_tracking();
    let location = use_location();
    
    Effect::new(move |_| {
        std::panic::catch_unwind(|| {
            let mut referral_data = ReferralData::default();
            let search_params = location.search.get();
            
            for param in search_params.split('&') {
                if let Some((key, value)) = param.split_once('=') {
                        match key {
                            "ref" | "referry" | "reffery" | "referral" => referral_data.referral_code = Some(value.to_string()),
                            "source" => referral_data.admin_referral_code = Some(value.to_string()),
                            _ => {}
                        }
                }
            }
            
            //web_sys::console::log_1(&format!("Collected referral data: {:?}", referral_data).into());
            
            if referral_data.referral_code.is_some() 
                || referral_data.admin_referral_code.is_some() {
                
                referral_context.referral_data.set(referral_data);
                
                web_sys::console::log_1(&format!("Referral data parsed from URL: {:?}", referral_context.referral_data.get()).into());
                log::info!("Referral data parsed from URL: {:?}", referral_context.referral_data.get());
            } else {
                web_sys::console::log_1(&"No referral data found in URL".into());
            }
        }).unwrap_or_else(|_| {
            log::warn!("Panic occurred while parsing referral data from URL");
        });
    });
    
    view! { }
}

/// Provider component for referral tracking context
#[component]
pub fn ReferralTrackingProvider(children: Children) -> impl IntoView {
    let context = ReferralTrackingContext::new();
    
    view! {
        <Provider value=context>
            {children()}
        </Provider>
    }
}
