use crate::i18n::use_i18n;
use leptos::prelude::*;
use shared::auth::{AuthResponse, User};
use std::rc::Rc;
#[cfg(target_arch = "wasm32")]
use wasm_bindgen_futures::spawn_local;

#[cfg(not(target_arch = "wasm32"))]
fn spawn_local<F>(_fut: F)
where
    F: std::future::Future<Output = ()> + 'static,
{
    // On server side, don't execute async operations that require browser APIs
}

#[derive(Clone, Debug)]
pub struct AuthState {
    pub user: Option<User>,
    pub is_loading: bool,
    pub error: Option<String>,
}

impl Default for AuthState {
    fn default() -> Self {
        Self {
            user: None,
            is_loading: false,
            error: None,
        }
    }
}

#[derive(Clone)]
pub struct AuthActions {
    pub login: Rc<dyn Fn(String, String, bool) -> ()>,
    pub logout: Rc<dyn Fn() -> ()>,
    pub register: Rc<dyn Fn(String, String, String, Option<String>) -> ()>,
    pub refresh_token: Rc<dyn Fn() -> ()>,
    pub update_profile: Rc<dyn Fn(String, Option<String>, Option<String>) -> ()>,
    pub change_password: Rc<dyn Fn(String, String) -> ()>,
    pub clear_error: Rc<dyn Fn() -> ()>,
}

#[derive(Clone)]
pub struct AuthContext {
    pub state: ReadSignal<AuthState>,
    pub actions: AuthActions,
}

impl AuthContext {
    pub fn is_authenticated(&self) -> bool {
        self.state.get().user.is_some()
    }

    pub fn is_loading(&self) -> bool {
        self.state.get().is_loading
    }

    pub fn user(&self) -> Option<User> {
        self.state.get().user
    }

    pub fn error(&self) -> Option<String> {
        self.state.get().error
    }

    pub fn has_role(&self, role: &shared::auth::Role) -> bool {
        self.state
            .get()
            .user
            .as_ref()
            .map_or(false, |user| user.has_role(role))
    }

    pub fn has_permission(&self, permission: &shared::auth::Permission) -> bool {
        self.state
            .get()
            .user
            .as_ref()
            .map_or(false, |user| user.has_permission(permission))
    }

    pub fn is_admin(&self) -> bool {
        self.state
            .get()
            .user
            .as_ref()
            .map_or(false, |user| user.is_admin())
    }
}

/// Helper function to get localized error message from server response
fn get_localized_error(error_text: &str, i18n: &crate::i18n::UseI18n) -> String {
    let error_lower = error_text.to_lowercase();

    let key = if error_lower.contains("invalid credentials") {
        "invalid-credentials"
    } else if error_lower.contains("user not found") {
        "user-not-found"
    } else if error_lower.contains("email already exists") {
        "email-already-exists"
    } else if error_lower.contains("username already exists") {
        "username-already-exists"
    } else if error_lower.contains("invalid token") {
        "invalid-token"
    } else if error_lower.contains("token expired") {
        "token-expired"
    } else if error_lower.contains("insufficient permissions") {
        "insufficient-permissions"
    } else if error_lower.contains("account not verified") {
        "account-not-verified"
    } else if error_lower.contains("account suspended") {
        "account-suspended"
    } else if error_lower.contains("rate limit exceeded") {
        "rate-limit-exceeded"
    } else if error_lower.contains("session expired") {
        "session-expired"
    } else if error_lower.contains("network") {
        "network-error"
    } else if error_lower.contains("login") && error_lower.contains("failed") {
        "login-failed"
    } else if error_lower.contains("registration") && error_lower.contains("failed") {
        "registration-failed"
    } else if error_lower.contains("profile") && error_lower.contains("failed") {
        "profile-update-failed"
    } else if error_lower.contains("password") && error_lower.contains("failed") {
        "password-change-failed"
    } else {
        "unknown-error"
    };

    i18n.t(key)
}

#[component]
pub fn AuthProvider(children: leptos::prelude::Children) -> impl IntoView {
    let i18n = use_i18n();
    let (state, set_state) = signal(AuthState::default());
    let (access_token, set_access_token) = signal::<Option<String>>(None);
    let (refresh_token_state, set_refresh_token) = signal::<Option<String>>(None);

    // Initialize auth state from localStorage - only in browser
    #[cfg(target_arch = "wasm32")]
    create_effect(move |_| {
        // Try to load stored tokens and user data
        if let Some(window) = web_sys::window() {
            if let Ok(Some(storage)) = window.local_storage() {
                // Load access token
                if let Ok(Some(token)) = storage.get_item("access_token") {
                    set_access_token.update(|t| *t = Some(token));
                }

                // Load refresh token
                if let Ok(Some(token)) = storage.get_item("refresh_token") {
                    set_refresh_token.update(|t| *t = Some(token));
                }

                // Load user data
                if let Ok(Some(user_data)) = storage.get_item("user") {
                    if let Ok(user) = serde_json::from_str::<User>(&user_data) {
                        set_state.update(|s| {
                            s.user = Some(user);
                        });
                    }
                }
            }
        }
    });

    let login_action = {
        let set_state = set_state.clone();
        let set_access_token = set_access_token.clone();
        let set_refresh_token = set_refresh_token.clone();
        let i18n = i18n.clone();

        Rc::new(move |email: String, password: String, remember_me: bool| {
            let set_state = set_state.clone();
            let set_access_token = set_access_token.clone();
            let set_refresh_token = set_refresh_token.clone();
            let i18n = i18n.clone();

            spawn_local(async move {
                set_state.update(|s| {
                    s.is_loading = true;
                    s.error = None;
                });

                let login_data = serde_json::json!({
                    "email": email,
                    "password": password,
                    "remember_me": remember_me
                });

                match reqwasm::http::Request::post("/api/auth/login")
                    .header("Content-Type", "application/json")
                    .body(login_data.to_string())
                    .send()
                    .await
                {
                    Ok(response) => {
                        if response.ok() {
                            match response.json::<serde_json::Value>().await {
                                Ok(json) => {
                                    if let Some(data) = json.get("data") {
                                        if let Ok(auth_response) =
                                            serde_json::from_value::<AuthResponse>(data.clone())
                                        {
                                            // Store tokens
                                            set_access_token.update(|t| {
                                                *t = Some(auth_response.access_token.clone())
                                            });
                                            if let Some(refresh_token) =
                                                &auth_response.refresh_token
                                            {
                                                set_refresh_token
                                                    .update(|t| *t = Some(refresh_token.clone()));
                                            }

                                            // Store in localStorage
                                            if let Some(window) = web_sys::window() {
                                                if let Ok(Some(storage)) = window.local_storage() {
                                                    let _ = storage.set_item(
                                                        "access_token",
                                                        &auth_response.access_token,
                                                    );
                                                    if let Some(refresh_token) =
                                                        &auth_response.refresh_token
                                                    {
                                                        let _ = storage.set_item(
                                                            "refresh_token",
                                                            refresh_token,
                                                        );
                                                    }
                                                    if let Ok(user_json) =
                                                        serde_json::to_string(&auth_response.user)
                                                    {
                                                        let _ =
                                                            storage.set_item("user", &user_json);
                                                    }
                                                }
                                            }

                                            set_state.update(|s| {
                                                s.user = Some(auth_response.user);
                                                s.is_loading = false;
                                            });
                                        }
                                    }
                                }
                                Err(_) => {
                                    set_state.update(|s| {
                                        s.error = Some(i18n.t("login-failed"));
                                        s.is_loading = false;
                                    });
                                }
                            }
                        } else {
                            let error_text = response
                                .text()
                                .await
                                .unwrap_or_else(|_| "Login failed".to_string());
                            let error_msg = get_localized_error(&error_text, &i18n);
                            set_state.update(|s| {
                                s.error = Some(error_msg);
                                s.is_loading = false;
                            });
                        }
                    }
                    Err(_) => {
                        set_state.update(|s| {
                            s.error = Some(i18n.t("network-error"));
                            s.is_loading = false;
                        });
                    }
                }
            });
        })
    };

    let logout_action = {
        let set_state = set_state.clone();
        let set_access_token = set_access_token.clone();
        let set_refresh_token = set_refresh_token.clone();

        Rc::new(move || {
            let set_state = set_state.clone();
            let set_access_token = set_access_token.clone();
            let set_refresh_token = set_refresh_token.clone();

            spawn_local(async move {
                // Call logout endpoint
                let _ = reqwasm::http::Request::post("/api/auth/logout")
                    .header(
                        "Authorization",
                        &format!("Bearer {}", access_token.get().unwrap_or_default()),
                    )
                    .send()
                    .await;

                // Clear local state
                set_state.update(|s| {
                    s.user = None;
                    s.error = None;
                    s.is_loading = false;
                });

                set_access_token.update(|t| *t = None);
                set_refresh_token.update(|t| *t = None);

                // Clear localStorage
                if let Some(window) = web_sys::window() {
                    if let Ok(Some(storage)) = window.local_storage() {
                        let _ = storage.remove_item("access_token");
                        let _ = storage.remove_item("refresh_token");
                        let _ = storage.remove_item("user");
                    }
                }
            });
        })
    };

    let register_action = {
        let set_state = set_state.clone();
        let set_access_token = set_access_token.clone();
        let set_refresh_token = set_refresh_token.clone();
        let i18n = i18n.clone();

        Rc::new(
            move |email: String,
                  username: String,
                  password: String,
                  display_name: Option<String>| {
                let set_state = set_state.clone();
                let set_access_token = set_access_token.clone();
                let set_refresh_token = set_refresh_token.clone();
                let i18n = i18n.clone();

                spawn_local(async move {
                    set_state.update(|s| {
                        s.is_loading = true;
                        s.error = None;
                    });

                    let register_data = serde_json::json!({
                        "email": email,
                        "username": username,
                        "password": password,
                        "display_name": display_name
                    });

                    match reqwasm::http::Request::post("/api/auth/register")
                        .header("Content-Type", "application/json")
                        .body(register_data.to_string())
                        .send()
                        .await
                    {
                        Ok(response) => {
                            if response.ok() {
                                match response.json::<serde_json::Value>().await {
                                    Ok(json) => {
                                        if let Some(data) = json.get("data") {
                                            if let Ok(auth_response) =
                                                serde_json::from_value::<AuthResponse>(data.clone())
                                            {
                                                // Store tokens and user data similar to login
                                                set_access_token.update(|t| {
                                                    *t = Some(auth_response.access_token.clone())
                                                });
                                                if let Some(refresh_token) =
                                                    &auth_response.refresh_token
                                                {
                                                    set_refresh_token.update(|t| {
                                                        *t = Some(refresh_token.clone())
                                                    });
                                                }

                                                // Store in localStorage
                                                if let Some(window) = web_sys::window() {
                                                    if let Ok(Some(storage)) =
                                                        window.local_storage()
                                                    {
                                                        let _ = storage.set_item(
                                                            "access_token",
                                                            &auth_response.access_token,
                                                        );
                                                        if let Some(refresh_token) =
                                                            &auth_response.refresh_token
                                                        {
                                                            let _ = storage.set_item(
                                                                "refresh_token",
                                                                refresh_token,
                                                            );
                                                        }
                                                        if let Ok(user_json) = serde_json::to_string(
                                                            &auth_response.user,
                                                        ) {
                                                            let _ = storage
                                                                .set_item("user", &user_json);
                                                        }
                                                    }
                                                }

                                                set_state.update(|s| {
                                                    s.user = Some(auth_response.user);
                                                    s.is_loading = false;
                                                });
                                            }
                                        }
                                    }
                                    Err(_) => {
                                        set_state.update(|s| {
                                            s.error = Some(i18n.t("registration-failed"));
                                            s.is_loading = false;
                                        });
                                    }
                                }
                            } else {
                                let error_text = response
                                    .text()
                                    .await
                                    .unwrap_or_else(|_| "Registration failed".to_string());
                                let error_msg = get_localized_error(&error_text, &i18n);
                                set_state.update(|s| {
                                    s.error = Some(error_msg);
                                    s.is_loading = false;
                                });
                            }
                        }
                        Err(_) => {
                            set_state.update(|s| {
                                s.error = Some(i18n.t("network-error"));
                                s.is_loading = false;
                            });
                        }
                    }
                });
            },
        )
    };

    let refresh_token_action = {
        let set_state = set_state.clone();
        let set_access_token = set_access_token.clone();
        let refresh_token_state = refresh_token_state.clone();
        let i18n = i18n.clone();

        Rc::new(move || {
            let set_state = set_state.clone();
            let set_access_token = set_access_token.clone();
            let refresh_token_state = refresh_token_state.clone();
            let i18n = i18n.clone();

            spawn_local(async move {
                if let Some(refresh_token) = refresh_token_state.get() {
                    let refresh_data = serde_json::json!({
                        "refresh_token": refresh_token
                    });

                    match reqwasm::http::Request::post("/api/auth/refresh")
                        .header("Content-Type", "application/json")
                        .body(refresh_data.to_string())
                        .send()
                        .await
                    {
                        Ok(response) => {
                            if response.ok() {
                                if let Ok(json) = response.json::<serde_json::Value>().await {
                                    if let Some(data) = json.get("data") {
                                        if let Ok(auth_response) =
                                            serde_json::from_value::<AuthResponse>(data.clone())
                                        {
                                            set_access_token.update(|t| {
                                                *t = Some(auth_response.access_token.clone())
                                            });

                                            // Update localStorage
                                            if let Some(window) = web_sys::window() {
                                                if let Ok(Some(storage)) = window.local_storage() {
                                                    let _ = storage.set_item(
                                                        "access_token",
                                                        &auth_response.access_token,
                                                    );
                                                }
                                            }
                                        }
                                    }
                                }
                            } else {
                                // Refresh failed, logout user
                                set_state.update(|s| {
                                    s.user = None;
                                    s.error = Some(i18n.t("session-expired"));
                                });
                            }
                        }
                        Err(_) => {
                            // Refresh failed, logout user
                            set_state.update(|s| {
                                s.user = None;
                                s.error = Some(i18n.t("session-expired"));
                            });
                        }
                    }
                }
            });
        })
    };

    let update_profile_action = {
        let set_state = set_state.clone();
        let access_token = access_token.clone();
        let i18n = i18n.clone();

        Rc::new(
            move |display_name: String, first_name: Option<String>, last_name: Option<String>| {
                let set_state = set_state.clone();
                let access_token = access_token.clone();
                let i18n = i18n.clone();

                spawn_local(async move {
                    set_state.update(|s| s.is_loading = true);

                    let update_data = serde_json::json!({
                        "display_name": display_name,
                        "first_name": first_name,
                        "last_name": last_name
                    });

                    match reqwasm::http::Request::put("/api/auth/profile")
                        .header("Content-Type", "application/json")
                        .header(
                            "Authorization",
                            &format!("Bearer {}", access_token.get().unwrap_or_default()),
                        )
                        .body(update_data.to_string())
                        .send()
                        .await
                    {
                        Ok(response) => {
                            if response.ok() {
                                if let Ok(json) = response.json::<serde_json::Value>().await {
                                    if let Some(data) = json.get("data") {
                                        if let Ok(user) =
                                            serde_json::from_value::<User>(data.clone())
                                        {
                                            set_state.update(|s| {
                                                s.user = Some(user.clone());
                                                s.is_loading = false;
                                            });

                                            // Update localStorage
                                            if let Some(window) = web_sys::window() {
                                                if let Ok(Some(storage)) = window.local_storage() {
                                                    if let Ok(user_json) =
                                                        serde_json::to_string(&user)
                                                    {
                                                        let _ =
                                                            storage.set_item("user", &user_json);
                                                    }
                                                }
                                            }
                                        }
                                    }
                                }
                            } else {
                                let error_text = response
                                    .text()
                                    .await
                                    .unwrap_or_else(|_| "Profile update failed".to_string());
                                let error_msg = get_localized_error(&error_text, &i18n);
                                set_state.update(|s| {
                                    s.error = Some(error_msg);
                                    s.is_loading = false;
                                });
                            }
                        }
                        Err(_) => {
                            set_state.update(|s| {
                                s.error = Some(i18n.t("network-error"));
                                s.is_loading = false;
                            });
                        }
                    }
                });
            },
        )
    };

    let change_password_action = {
        let set_state = set_state.clone();
        let access_token = access_token.clone();
        let i18n = i18n.clone();

        Rc::new(move |current_password: String, new_password: String| {
            let set_state = set_state.clone();
            let access_token = access_token.clone();
            let i18n = i18n.clone();

            spawn_local(async move {
                set_state.update(|s| s.is_loading = true);

                let change_data = serde_json::json!({
                    "current_password": current_password,
                    "new_password": new_password
                });

                match reqwasm::http::Request::post("/api/auth/change-password")
                    .header("Content-Type", "application/json")
                    .header(
                        "Authorization",
                        &format!("Bearer {}", access_token.get().unwrap_or_default()),
                    )
                    .body(change_data.to_string())
                    .send()
                    .await
                {
                    Ok(response) => {
                        if response.ok() {
                            set_state.update(|s| {
                                s.is_loading = false;
                            });
                        } else {
                            let error_text = response
                                .text()
                                .await
                                .unwrap_or_else(|_| "Password change failed".to_string());
                            let error_msg = get_localized_error(&error_text, &i18n);
                            set_state.update(|s| {
                                s.error = Some(error_msg);
                                s.is_loading = false;
                            });
                        }
                    }
                    Err(_) => {
                        set_state.update(|s| {
                            s.error = Some(i18n.t("network-error"));
                            s.is_loading = false;
                        });
                    }
                }
            });
        })
    };

    let clear_error_action = {
        let set_state = set_state.clone();
        Rc::new(move || {
            set_state.update(|s| s.error = None);
        })
    };

    let actions = AuthActions {
        login: login_action,
        logout: logout_action,
        register: register_action,
        refresh_token: refresh_token_action,
        update_profile: update_profile_action,
        change_password: change_password_action,
        clear_error: clear_error_action,
    };

    let context = AuthContext {
        state: state.into(),
        actions,
    };

    provide_context(context);

    view! {
        <div>
            {children()}
        </div>
    }
}

#[derive(Clone)]
pub struct UseAuth(pub AuthContext);

impl UseAuth {
    pub fn new() -> Self {
        Self(expect_context::<AuthContext>())
    }
}

pub fn use_auth() -> UseAuth {
    UseAuth::new()
}
