// Frontend authentication module - connects to backend API
use serde::{Deserialize, Serialize};
use wasm_bindgen::JsCast;
use wasm_bindgen_futures::JsFuture;
use web_sys::{Request, RequestInit, RequestMode, Response};

#[derive(Debug, Serialize, Deserialize)]
pub struct CalDAVLoginRequest {
    pub server_url: String,
    pub username: String,
    pub password: String,
}

#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct UserPreferencesResponse {
    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>,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct AuthResponse {
    pub token: String,
    pub session_token: String,
    pub username: String,
    pub server_url: String,
    pub preferences: UserPreferencesResponse,
}

#[derive(Debug, Deserialize)]
pub struct ApiErrorResponse {
    pub error: String,
}

// Frontend auth service - connects to backend API
pub struct AuthService {
    base_url: String,
}

impl AuthService {
    pub fn new() -> Self {
        // Get backend URL from environment variable at compile time, fallback to localhost
        let base_url = option_env!("BACKEND_API_URL")
            .unwrap_or("http://localhost:3000/api")
            .to_string();

        Self { base_url }
    }

    pub async fn login(&self, request: CalDAVLoginRequest) -> Result<AuthResponse, String> {
        self.post_json("/auth/login", &request).await
    }

    pub async fn verify_token(&self, token: &str) -> Result<bool, String> {
        let window = web_sys::window().ok_or("No global window exists")?;

        let opts = RequestInit::new();
        opts.set_method("GET");
        opts.set_mode(RequestMode::Cors);

        let url = format!("{}/auth/verify", self.base_url);
        let request = Request::new_with_str_and_init(&url, &opts)
            .map_err(|e| format!("Request creation failed: {:?}", e))?;

        request
            .headers()
            .set("Authorization", &format!("Bearer {}", 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() {
            let text = JsFuture::from(
                resp.text()
                    .map_err(|e| format!("Text extraction failed: {:?}", e))?,
            )
            .await
            .map_err(|e| format!("Text promise failed: {:?}", e))?;

            let text_string = text.as_string().ok_or("Response text is not a string")?;
            
            // Parse the response to get the "valid" field
            let response: serde_json::Value = serde_json::from_str(&text_string)
                .map_err(|e| format!("JSON parsing failed: {}", e))?;
            
            Ok(response.get("valid").and_then(|v| v.as_bool()).unwrap_or(false))
        } else {
            Ok(false) // Invalid token
        }
    }

    // Helper method for POST requests with JSON body
    async fn post_json<T: Serialize, R: for<'de> Deserialize<'de>>(
        &self,
        endpoint: &str,
        body: &T,
    ) -> Result<R, String> {
        let window = web_sys::window().ok_or("No global window exists")?;

        let json_body =
            serde_json::to_string(body).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!("{}{}", self.base_url, endpoint);
        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))?;

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

        let text = JsFuture::from(
            resp.text()
                .map_err(|e| format!("Text extraction failed: {:?}", e))?,
        )
        .await
        .map_err(|e| format!("Text promise failed: {:?}", e))?;

        let text_string = text.as_string().ok_or("Response text is not a string")?;

        if resp.ok() {
            serde_json::from_str::<R>(&text_string)
                .map_err(|e| format!("JSON parsing failed: {}", e))
        } else {
            // Try to parse error response
            if let Ok(error_response) = serde_json::from_str::<ApiErrorResponse>(&text_string) {
                Err(error_response.error)
            } else {
                Err(format!("Request failed with status {}", resp.status()))
            }
        }
    }
}
