use crate::i18n::use_i18n;
use leptos::prelude::*;
use rustelo_core_lib::auth::{AuthResponse, User, UserProfile};
use std::rc::Rc;
use std::collections::HashMap;
use uuid::Uuid;


#[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: &rustelo_core_lib::auth::Role) -> bool {
    self.state
      .get()
      .user
      .as_ref()
      .map_or(false, |user| user.has_role(role))
  }

  pub fn has_permission(&self, permission: &rustelo_core_lib::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 - delayed to avoid SSR spawn_local issues
  #[cfg(target_arch = "wasm32")]
  {
    gloo_timers::callback::Timeout::new(50, move || {
      // Try to load stored tokens and user data after hydration
      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);
              });
            }
          }
        }
      }
    }).forget();
  }

  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();

      #[cfg(target_arch = "wasm32")]
      {
        // Set loading state immediately
        set_state.update(|s| {
          s.is_loading = true;
          s.error = None;
        });

        // Simulate login with timeout - no spawn_local
        gloo_timers::callback::Timeout::new(1000, move || {
          // Create mock user data
          let mock_user = User {
            id: Uuid::new_v4(),
            email: email.clone(),
            username: email.split('@').next().unwrap_or("demo").to_string(),
            display_name: Some(format!("Demo User ({})", email)),
            avatar_url: None,
            roles: vec![rustelo_core_lib::auth::Role::User],
            is_active: true,
            email_verified: true,
            created_at: chrono::Utc::now(),
            updated_at: chrono::Utc::now(),
            last_login: Some(chrono::Utc::now()),
            profile: UserProfile {
              first_name: Some("Demo".to_string()),
              last_name: Some("User".to_string()),
              bio: None,
              timezone: None,
              locale: None,
              preferences: HashMap::new(),
              categories: vec![],
              tags: vec![],
            },
            two_factor_enabled: false,
          };

          let mock_access_token = "demo_access_token_123456789".to_string();
          let mock_refresh_token = if remember_me {
            Some("demo_refresh_token_987654321".to_string())
          } else {
            None
          };

          // Simulate login failure for demo@fail.com
          if email == "demo@fail.com" {
            set_state.update(|s| {
              s.error = Some(i18n.t("invalid-credentials"));
              s.is_loading = false;
            });
            return;
          }

          // Store tokens
          set_access_token.update(|t| *t = Some(mock_access_token.clone()));
          if let Some(refresh_token) = &mock_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", &mock_access_token);
              if let Some(refresh_token) = &mock_refresh_token {
                let _ = storage.set_item("refresh_token", refresh_token);
              }
              if let Ok(user_json) = serde_json::to_string(&mock_user) {
                let _ = storage.set_item("user", &user_json);
              }
            }
          }

          // Update state with mock user
          set_state.update(|s| {
            s.user = Some(mock_user);
            s.is_loading = false;
          });
        }).forget();
      }
    })
  };

  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();

      #[cfg(target_arch = "wasm32")]
      {
        // Simulate logout with timeout - no spawn_local
        gloo_timers::callback::Timeout::new(300, move || {
          // 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");
            }
          }
        }).forget();
      }
    })
  };

  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();

        #[cfg(target_arch = "wasm32")]
        {
          // Set loading state immediately
          set_state.update(|s| {
            s.is_loading = true;
            s.error = None;
          });

          // Simulate registration with timeout - no spawn_local
          gloo_timers::callback::Timeout::new(1200, move || {
            // Simulate email already exists error
            if email == "existing@example.com" {
              set_state.update(|s| {
                s.error = Some(i18n.t("email-already-exists"));
                s.is_loading = false;
              });
              return;
            }

            // Simulate username already exists error
            if username == "existinguser" {
              set_state.update(|s| {
                s.error = Some(i18n.t("username-already-exists"));
                s.is_loading = false;
              });
              return;
            }

            // Create mock user data
            let mock_user = User {
              id: Uuid::new_v4(),
              email: email.clone(),
              username: username.clone(),
              display_name: display_name.clone().or_else(|| Some(username.clone())),
              avatar_url: None,
              roles: vec![rustelo_core_lib::auth::Role::User],
              is_active: true,
              email_verified: false, // New registrations start unverified
              created_at: chrono::Utc::now(),
              updated_at: chrono::Utc::now(),
              last_login: None,
              profile: UserProfile {
                first_name: None,
                last_name: None,
                bio: None,
                timezone: None,
                locale: None,
                preferences: HashMap::new(),
                categories: vec![],
                tags: vec![],
              },
              two_factor_enabled: false,
            };

            let mock_access_token = "demo_access_token_reg_123456789".to_string();
            let mock_refresh_token = Some("demo_refresh_token_reg_987654321".to_string());

            // Store tokens
            set_access_token.update(|t| *t = Some(mock_access_token.clone()));
            if let Some(refresh_token) = &mock_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", &mock_access_token);
                if let Some(refresh_token) = &mock_refresh_token {
                  let _ = storage.set_item("refresh_token", refresh_token);
                }
                if let Ok(user_json) = serde_json::to_string(&mock_user) {
                  let _ = storage.set_item("user", &user_json);
                }
              }
            }

            // Update state with mock user
            set_state.update(|s| {
              s.user = Some(mock_user);
              s.is_loading = false;
            });
          }).forget();
        }
      },
    )
  };

  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();

      #[cfg(target_arch = "wasm32")]
      {
        // Simulate token refresh with timeout - no spawn_local
        gloo_timers::callback::Timeout::new(500, move || {
          if let Some(_refresh_token) = refresh_token_state.get() {
            // Simulate successful token refresh most of the time
            let should_succeed = true;

            if should_succeed {
              let new_access_token = "demo_refreshed_access_token_123456789".to_string();

              set_access_token.update(|t| *t = Some(new_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", &new_access_token);
                }
              }
            } else {
              // Simulate refresh failure - logout user
              set_state.update(|s| {
                s.user = None;
                s.error = Some(i18n.t("session-expired"));
              });
            }
          } else {
            // No refresh token available - logout user
            set_state.update(|s| {
              s.user = None;
              s.error = Some(i18n.t("session-expired"));
            });
          }
        }).forget();
      }
    })
  };

  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();

        #[cfg(target_arch = "wasm32")]
        {
          // Set loading state immediately
          set_state.update(|s| {
            s.is_loading = true;
            s.error = None;
          });

          // Simulate profile update with timeout - no spawn_local
          gloo_timers::callback::Timeout::new(800, move || {
            // Check if user is authenticated
            if access_token.get().is_none() {
              set_state.update(|s| {
                s.error = Some(i18n.t("session-expired"));
                s.is_loading = false;
              });
              return;
            }

            // Get current user and update it
            let current_state = set_state.get();
            if let Some(mut user) = current_state.user {
              user.display_name = Some(display_name);
              user.profile.first_name = first_name;
              user.profile.last_name = last_name;
              user.updated_at = chrono::Utc::now();

              // 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);
                  }
                }
              }

              // Update state with modified user
              set_state.update(|s| {
                s.user = Some(user);
                s.is_loading = false;
              });
            } else {
              set_state.update(|s| {
                s.error = Some(i18n.t("profile-update-failed"));
                s.is_loading = false;
              });
            }
          }).forget();
        }
      },
    )
  };

  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();

      #[cfg(target_arch = "wasm32")]
      {
        // Set loading state immediately
        set_state.update(|s| {
          s.is_loading = true;
          s.error = None;
        });

        // Simulate password change with timeout - no spawn_local
        gloo_timers::callback::Timeout::new(1000, move || {
          // Check if user is authenticated
          if access_token.get().is_none() {
            set_state.update(|s| {
              s.error = Some(i18n.t("session-expired"));
              s.is_loading = false;
            });
            return;
          }

          // Simulate current password validation failure
          if current_password == "wrongpassword" {
            set_state.update(|s| {
              s.error = Some(i18n.t("invalid-credentials"));
              s.is_loading = false;
            });
            return;
          }

          // Simulate weak password error
          if new_password.len() < 8 {
            set_state.update(|s| {
              s.error = Some("Password must be at least 8 characters long".to_string());
              s.is_loading = false;
            });
            return;
          }

          // Simulate successful password change
          set_state.update(|s| {
            s.is_loading = false;
            s.error = None;
          });
        }).forget();
      }
    })
  };

  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()
}
