use base64::prelude::*;
use serde::{Deserialize, Serialize};
use std::env;

/// Configuration for CalDAV server connection and authentication.
///
/// This struct holds all the necessary information to connect to a CalDAV server,
/// including server URL, credentials, and optional collection paths.
///
/// # Security Note
///
/// The password field contains sensitive information and should be handled carefully.
/// This struct implements `Debug` but in production, consider implementing a custom
/// `Debug` that masks the password field.
///
/// # Example
///
/// ```rust
/// # use calendar_backend::config::CalDAVConfig;
/// let config = CalDAVConfig {
///     server_url: "https://caldav.example.com".to_string(),
///     username: "user@example.com".to_string(),
///     password: "password".to_string(),
///     calendar_path: None,
///     tasks_path: None,
/// };
///
/// // Use the configuration for HTTP requests
/// let auth_header = format!("Basic {}", config.get_basic_auth());
/// ```
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CalDAVConfig {
    /// The base URL of the CalDAV server (e.g., "https://caldav.example.com/dav/")
    pub server_url: String,

    /// Username for authentication with the CalDAV server
    pub username: String,

    /// Password for authentication with the CalDAV server
    ///
    /// **Security Note**: This contains sensitive information
    pub password: String,

    /// Optional path to the calendar collection on the server
    ///
    /// If not provided, the client will discover available calendars
    /// through CalDAV PROPFIND requests
    pub calendar_path: Option<String>,
}

impl CalDAVConfig {
    /// Creates a new CalDAVConfig with the given credentials.
    ///
    /// # Arguments
    ///
    /// * `server_url` - The base URL of the CalDAV server
    /// * `username` - Username for authentication
    /// * `password` - Password for authentication
    ///
    /// # Example
    ///
    /// ```rust
    /// # use calendar_backend::config::CalDAVConfig;
    /// let config = CalDAVConfig::new(
    ///     "https://caldav.example.com".to_string(),
    ///     "user@example.com".to_string(),
    ///     "password".to_string()
    /// );
    /// ```
    pub fn new(server_url: String, username: String, password: String) -> Self {
        Self {
            server_url,
            username,
            password,
            calendar_path: env::var("CALDAV_CALENDAR_PATH").ok(), // Optional override from env
        }
    }

    /// Generates a Base64-encoded string for HTTP Basic Authentication.
    ///
    /// This method combines the username and password in the format
    /// `username:password` and encodes it using Base64, which is the
    /// standard format for the `Authorization: Basic` HTTP header.
    ///
    /// # Returns
    ///
    /// A Base64-encoded string that can be used directly in the
    /// `Authorization` header: `Authorization: Basic <returned_value>`
    ///
    /// # Example
    ///
    /// ```rust
    /// # use calendar_backend::config::CalDAVConfig;
    ///
    /// let config = CalDAVConfig {
    ///     server_url: "https://example.com".to_string(),
    ///     username: "user".to_string(),
    ///     password: "pass".to_string(),
    ///     calendar_path: None,
    ///     tasks_path: None,
    /// };
    ///
    /// let auth_value = config.get_basic_auth();
    /// let auth_header = format!("Basic {}", auth_value);
    /// ```
    pub fn get_basic_auth(&self) -> String {
        let credentials = format!("{}:{}", self.username, self.password);
        BASE64_STANDARD.encode(&credentials)
    }
}

/// Errors that can occur when loading or using CalDAV configuration.
#[derive(Debug, thiserror::Error)]
pub enum ConfigError {
    /// A required environment variable is missing or cannot be read.
    ///
    /// This error occurs when calling `CalDAVConfig::from_env()` and one of the
    /// required environment variables (`CALDAV_SERVER_URL`, `CALDAV_USERNAME`,
    /// or `CALDAV_PASSWORD`) is not set.
    #[error("Missing environment variable: {0}")]
    MissingVar(String),

    /// The configuration contains invalid or malformed values.
    ///
    /// This could include malformed URLs, invalid authentication credentials,
    /// or other configuration issues that prevent proper CalDAV operation.
    #[error("Invalid configuration: {0}")]
    Invalid(String),
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_basic_auth_encoding() {
        let config = CalDAVConfig {
            server_url: "https://example.com".to_string(),
            username: "testuser".to_string(),
            password: "testpass".to_string(),
            calendar_path: None,
        };

        let auth = config.get_basic_auth();
        let expected = BASE64_STANDARD.encode("testuser:testpass");
        assert_eq!(auth, expected);
    }

    /// Integration test that authenticates with the actual Baikal CalDAV server
    ///
    /// This test requires a valid .env file with:
    /// - CALDAV_SERVER_URL
    /// - CALDAV_USERNAME  
    /// - CALDAV_PASSWORD
    ///
    /// Run with: `cargo test test_baikal_auth`
    #[tokio::test]
    async fn test_baikal_auth() {
        // Use test config - update these values to test with real server
        let config = CalDAVConfig::new(
            "https://example.com".to_string(),
            "test_user".to_string(),
            "test_password".to_string(),
        );

        println!("Testing authentication to: {}", config.server_url);

        // Create HTTP client
        let client = reqwest::Client::new();

        // Make a simple OPTIONS request to test authentication
        let response = client
            .request(reqwest::Method::OPTIONS, &config.server_url)
            .header(
                "Authorization",
                format!("Basic {}", config.get_basic_auth()),
            )
            .header("User-Agent", "calendar-app/0.1.0")
            .send()
            .await
            .expect("Failed to send request to CalDAV server");

        println!("Response status: {}", response.status());
        println!("Response headers: {:#?}", response.headers());

        // Check if we got a successful response or at least not a 401 Unauthorized
        assert!(
            response.status().is_success() || response.status() != 401,
            "Authentication failed with status: {}. Check your credentials in .env",
            response.status()
        );

        // For Baikal/CalDAV servers, we should see DAV headers
        assert!(
            response.headers().contains_key("dav")
                || response.headers().contains_key("DAV")
                || response.status().is_success(),
            "Server doesn't appear to be a CalDAV server - missing DAV headers"
        );

        println!("✓ Authentication test passed!");
    }

    /// Test making a PROPFIND request to discover calendars
    ///
    /// This test requires a valid .env file and makes an actual CalDAV PROPFIND request
    ///
    /// Run with: `cargo test test_propfind_calendars`
    #[tokio::test]
    async fn test_propfind_calendars() {
        // Use test config - update these values to test with real server
        let config = CalDAVConfig::new(
            "https://example.com".to_string(),
            "test_user".to_string(),
            "test_password".to_string(),
        );

        let client = reqwest::Client::new();

        // CalDAV PROPFIND request to discover calendars
        let propfind_body = r#"<?xml version="1.0" encoding="utf-8" ?>
<d:propfind xmlns:d="DAV:" xmlns:c="urn:ietf:params:xml:ns:caldav">
    <d:prop>
        <d:resourcetype />
        <d:displayname />
        <c:calendar-description />
        <c:supported-calendar-component-set />
    </d:prop>
</d:propfind>"#;

        let response = client
            .request(
                reqwest::Method::from_bytes(b"PROPFIND").unwrap(),
                &config.server_url,
            )
            .header(
                "Authorization",
                format!("Basic {}", config.get_basic_auth()),
            )
            .header("Content-Type", "application/xml")
            .header("Depth", "1")
            .header("User-Agent", "calendar-app/0.1.0")
            .body(propfind_body)
            .send()
            .await
            .expect("Failed to send PROPFIND request");

        let status = response.status();
        println!("PROPFIND Response status: {}", status);

        let body = response.text().await.expect("Failed to read response body");
        println!("PROPFIND Response body: {}", body);

        // We should get a 207 Multi-Status for PROPFIND
        assert_eq!(
            status,
            reqwest::StatusCode::from_u16(207).unwrap(),
            "PROPFIND should return 207 Multi-Status"
        );

        // The response should contain XML with calendar information
        assert!(
            body.contains("calendar"),
            "Response should contain calendar information"
        );

        println!("✓ PROPFIND calendars test passed!");
    }
}
