use axum::{
    response::Json,
    routing::{get, post},
    Router,
};
use calendar_backend::auth::AuthService;
use calendar_backend::AppState;
use reqwest::Client;
use serde_json::json;
use std::sync::Arc;
use std::time::Duration;
use tokio::time::sleep;
use tower_http::cors::{Any, CorsLayer};

/// Test utilities for integration testing
mod test_utils {
    use super::*;

    pub struct TestServer {
        pub base_url: String,
        pub client: Client,
    }

    impl TestServer {
        pub async fn start() -> Self {
            // Create auth service
            let jwt_secret = "test-secret-key-for-integration-tests".to_string();
            let auth_service = AuthService::new(jwt_secret);
            let app_state = AppState { auth_service };

            // Build application with routes
            let app = Router::new()
                .route("/", get(root))
                .route("/api/health", get(health_check))
                .route("/api/auth/login", post(calendar_backend::handlers::login))
                .route(
                    "/api/auth/verify",
                    get(calendar_backend::handlers::verify_token),
                )
                .route(
                    "/api/user/info",
                    get(calendar_backend::handlers::get_user_info),
                )
                .route(
                    "/api/calendar/create",
                    post(calendar_backend::handlers::create_calendar),
                )
                .route(
                    "/api/calendar/delete",
                    post(calendar_backend::handlers::delete_calendar),
                )
                .route(
                    "/api/calendar/events",
                    get(calendar_backend::handlers::get_calendar_events),
                )
                .route(
                    "/api/calendar/events/create",
                    post(calendar_backend::handlers::create_event),
                )
                .route(
                    "/api/calendar/events/update",
                    post(calendar_backend::handlers::update_event),
                )
                .route(
                    "/api/calendar/events/delete",
                    post(calendar_backend::handlers::delete_event),
                )
                .route(
                    "/api/calendar/events/:uid",
                    get(calendar_backend::handlers::refresh_event),
                )
                // Event series-specific endpoints
                .route(
                    "/api/calendar/events/series/create",
                    post(calendar_backend::handlers::create_event_series),
                )
                .route(
                    "/api/calendar/events/series/update",
                    post(calendar_backend::handlers::update_event_series),
                )
                .route(
                    "/api/calendar/events/series/delete",
                    post(calendar_backend::handlers::delete_event_series),
                )
                .layer(
                    CorsLayer::new()
                        .allow_origin(Any)
                        .allow_methods(Any)
                        .allow_headers(Any),
                )
                .with_state(Arc::new(app_state));

            // Start server on a random port
            let listener = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap();
            let addr = listener.local_addr().unwrap();
            let base_url = format!("http://127.0.0.1:{}", addr.port());

            tokio::spawn(async move {
                axum::serve(listener, app).await.unwrap();
            });

            // Wait for server to start
            sleep(Duration::from_millis(100)).await;

            let client = Client::new();
            TestServer { base_url, client }
        }

        pub async fn login(&self) -> String {
            let login_payload = json!({
                "username": "test".to_string(),
                "password": "test".to_string(),
                "server_url": "https://example.com".to_string()
            });

            let response = self
                .client
                .post(&format!("{}/api/auth/login", self.base_url))
                .json(&login_payload)
                .send()
                .await
                .expect("Failed to send login request");

            assert!(
                response.status().is_success(),
                "Login failed with status: {}",
                response.status()
            );

            let login_response: serde_json::Value = response.json().await.unwrap();
            login_response["token"]
                .as_str()
                .expect("Login response should contain token")
                .to_string()
        }
    }

    async fn root() -> &'static str {
        "Calendar Backend API v0.1.0"
    }

    async fn health_check() -> Json<serde_json::Value> {
        Json(serde_json::json!({
            "status": "healthy",
            "service": "calendar-backend",
            "version": "0.1.0"
        }))
    }
}

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

    /// Test the health endpoint
    #[tokio::test]
    async fn test_health_endpoint() {
        let server = TestServer::start().await;

        let response = server
            .client
            .get(&format!("{}/api/health", server.base_url))
            .send()
            .await
            .unwrap();

        assert_eq!(response.status(), 200);

        let health_response: serde_json::Value = response.json().await.unwrap();
        assert_eq!(health_response["status"], "healthy");
        assert_eq!(health_response["service"], "calendar-backend");

        println!("✓ Health endpoint test passed");
    }

    /// Test authentication login endpoint
    #[tokio::test]
    async fn test_auth_login() {
        let server = TestServer::start().await;

        // Use test credentials
        let username = "test".to_string();
        let password = "test".to_string();
        let server_url = "https://example.com".to_string();

        let login_payload = json!({
            "username": username,
            "password": password,
            "server_url": server_url
        });

        let response = server
            .client
            .post(&format!("{}/api/auth/login", server.base_url))
            .json(&login_payload)
            .send()
            .await
            .unwrap();

        assert!(
            response.status().is_success(),
            "Login failed with status: {}",
            response.status()
        );

        let login_response: serde_json::Value = response.json().await.unwrap();
        assert!(
            login_response["token"].is_string(),
            "Login response should contain a token"
        );
        assert!(
            login_response["username"].is_string(),
            "Login response should contain username"
        );

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

    /// Test authentication verify endpoint  
    #[tokio::test]
    async fn test_auth_verify() {
        let server = TestServer::start().await;

        // First login to get a token
        let token = server.login().await;

        let response = server
            .client
            .get(&format!("{}/api/auth/verify", server.base_url))
            .header("Authorization", format!("Bearer {}", token))
            .send()
            .await
            .unwrap();

        assert_eq!(response.status(), 200);

        let verify_response: serde_json::Value = response.json().await.unwrap();
        assert!(verify_response["valid"].as_bool().unwrap_or(false));

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

    /// Test user info endpoint
    #[tokio::test]
    async fn test_user_info() {
        let server = TestServer::start().await;

        // First login to get a token
        let token = server.login().await;

        // Load password from env for CalDAV requests
        dotenvy::dotenv().ok();
        let password = "test".to_string();

        let response = server
            .client
            .get(&format!("{}/api/user/info", server.base_url))
            .header("Authorization", format!("Bearer {}", token))
            .header("X-CalDAV-Password", password)
            .send()
            .await
            .unwrap();

        // Note: This might fail if CalDAV server discovery fails, which can happen
        if response.status().is_success() {
            let user_info: serde_json::Value = response.json().await.unwrap();
            assert!(user_info["username"].is_string());
            println!("✓ User info test passed");
        } else {
            println!(
                "⚠ User info test skipped (CalDAV server issues): {}",
                response.status()
            );
        }
    }

    /// Test calendar events listing endpoint
    #[tokio::test]
    async fn test_get_calendar_events() {
        let server = TestServer::start().await;

        // First login to get a token
        let token = server.login().await;

        // Load password from env for CalDAV requests
        dotenvy::dotenv().ok();
        let password = "test".to_string();

        let response = server
            .client
            .get(&format!(
                "{}/api/calendar/events?year=2024&month=12",
                server.base_url
            ))
            .header("Authorization", format!("Bearer {}", token))
            .header("X-CalDAV-Password", password)
            .send()
            .await
            .unwrap();

        assert!(
            response.status().is_success(),
            "Get events failed with status: {}",
            response.status()
        );

        let events: serde_json::Value = response.json().await.unwrap();
        assert!(events.is_array());

        println!(
            "✓ Get calendar events test passed (found {} events)",
            events.as_array().unwrap().len()
        );
    }

    /// Test event creation endpoint
    #[tokio::test]
    async fn test_create_event() {
        let server = TestServer::start().await;

        // First login to get a token
        let token = server.login().await;

        // Load password from env for CalDAV requests
        dotenvy::dotenv().ok();
        let password = "test".to_string();

        let create_payload = json!({
            "title": "Integration Test Event",
            "description": "Created by integration test",
            "start_date": "2024-12-25",
            "start_time": "10:00",
            "end_date": "2024-12-25",
            "end_time": "11:00",
            "location": "Test Location",
            "all_day": false,
            "status": "confirmed",
            "class": "public",
            "priority": 5,
            "organizer": "test@example.com",
            "attendees": "",
            "categories": "test",
            "reminder": "15min",
            "recurrence": "none",
            "recurrence_days": [false, false, false, false, false, false, false]
        });

        let response = server
            .client
            .post(&format!("{}/api/calendar/events/create", server.base_url))
            .header("Authorization", format!("Bearer {}", token))
            .header("X-CalDAV-Password", password)
            .json(&create_payload)
            .send()
            .await
            .unwrap();

        let status = response.status();
        println!("Create event response status: {}", status);

        // Note: This might fail if CalDAV server is not accessible, which is expected in CI
        if status.is_success() {
            let create_response: serde_json::Value = response.json().await.unwrap();
            assert!(create_response["success"].as_bool().unwrap_or(false));
            println!("✓ Create event test passed");
        } else {
            println!("⚠ Create event test skipped (CalDAV server not accessible)");
        }
    }

    /// Test event refresh endpoint
    #[tokio::test]
    async fn test_refresh_event() {
        let server = TestServer::start().await;

        // First login to get a token
        let token = server.login().await;

        // Load password from env for CalDAV requests
        dotenvy::dotenv().ok();
        let password = "test".to_string();

        // Use a dummy UID for testing - this will likely return 404 but we're testing the endpoint structure
        let test_uid = "test-event-uid";

        let response = server
            .client
            .get(&format!(
                "{}/api/calendar/events/{}",
                server.base_url, test_uid
            ))
            .header("Authorization", format!("Bearer {}", token))
            .header("X-CalDAV-Password", password)
            .send()
            .await
            .unwrap();

        // We expect either 200 (if event exists) or 404 (if not found) - both are valid responses
        assert!(
            response.status() == 200 || response.status() == 404,
            "Refresh event failed with unexpected status: {}",
            response.status()
        );

        println!("✓ Refresh event endpoint test passed");
    }

    /// Test invalid authentication
    #[tokio::test]
    async fn test_invalid_auth() {
        let server = TestServer::start().await;

        let response = server
            .client
            .get(&format!("{}/api/user/info", server.base_url))
            .header("Authorization", "Bearer invalid-token")
            .send()
            .await
            .unwrap();

        // Accept both 400 and 401 as valid responses for invalid tokens
        assert!(
            response.status() == 401 || response.status() == 400,
            "Expected 401 or 400 for invalid token, got {}",
            response.status()
        );
        println!("✓ Invalid authentication test passed");
    }

    /// Test missing authentication
    #[tokio::test]
    async fn test_missing_auth() {
        let server = TestServer::start().await;

        let response = server
            .client
            .get(&format!("{}/api/user/info", server.base_url))
            .send()
            .await
            .unwrap();

        assert_eq!(response.status(), 401);
        println!("✓ Missing authentication test passed");
    }

    // ==================== EVENT SERIES TESTS ====================

    /// Test event series creation endpoint
    #[tokio::test]
    async fn test_create_event_series() {
        let server = TestServer::start().await;

        // First login to get a token
        let token = server.login().await;

        // Load password from env for CalDAV requests
        dotenvy::dotenv().ok();
        let password = "test".to_string();

        let create_payload = json!({
            "title": "Integration Test Series",
            "description": "Created by integration test for series",
            "start_date": "2024-12-25",
            "start_time": "10:00",
            "end_date": "2024-12-25",
            "end_time": "11:00",
            "location": "Test Series Location",
            "all_day": false,
            "status": "confirmed",
            "class": "public",
            "priority": 5,
            "organizer": "test@example.com",
            "attendees": "",
            "categories": "test-series",
            "reminder": "15min",
            "recurrence": "weekly",
            "recurrence_days": [false, true, false, false, false, false, false], // Monday only
            "recurrence_interval": 1,
            "recurrence_count": 4,
            "calendar_path": "/calendars/test/default/" // Provide explicit path to bypass discovery
        });

        let response = server
            .client
            .post(&format!(
                "{}/api/calendar/events/series/create",
                server.base_url
            ))
            .header("Authorization", format!("Bearer {}", token))
            .header("X-CalDAV-Password", password)
            .json(&create_payload)
            .send()
            .await
            .unwrap();

        let status = response.status();
        println!("Create series response status: {}", status);

        // Note: This might fail if CalDAV server is not accessible, which is expected in CI
        if status.is_success() {
            let create_response: serde_json::Value = response.json().await.unwrap();
            assert!(create_response["success"].as_bool().unwrap_or(false));
            assert!(create_response["series_uid"].is_string());
            println!("✓ Create event series test passed");
        } else {
            println!("⚠ Create event series test skipped (CalDAV server not accessible)");
        }
    }

    /// Test event series update endpoint
    #[tokio::test]
    async fn test_update_event_series() {
        let server = TestServer::start().await;

        // First login to get a token
        let token = server.login().await;

        // Load password from env for CalDAV requests
        dotenvy::dotenv().ok();
        let password = "test".to_string();

        let update_payload = json!({
            "series_uid": "test-series-uid",
            "title": "Updated Series Title",
            "description": "Updated by integration test",
            "start_date": "2024-12-26",
            "start_time": "14:00",
            "end_date": "2024-12-26",
            "end_time": "15:00",
            "location": "Updated Location",
            "all_day": false,
            "status": "confirmed",
            "class": "public",
            "priority": 3,
            "organizer": "test@example.com",
            "attendees": "attendee@example.com",
            "categories": "updated-series",
            "reminder": "30min",
            "recurrence": "daily",
            "recurrence_days": [false, false, false, false, false, false, false],
            "recurrence_interval": 2,
            "recurrence_count": 10,
            "update_scope": "all_in_series",
            "calendar_path": "/calendars/test/default/" // Provide explicit path to bypass discovery
        });

        let response = server
            .client
            .post(&format!(
                "{}/api/calendar/events/series/update",
                server.base_url
            ))
            .header("Authorization", format!("Bearer {}", token))
            .header("X-CalDAV-Password", password)
            .json(&update_payload)
            .send()
            .await
            .unwrap();

        let status = response.status();
        println!("Update series response status: {}", status);

        // Note: This might fail if CalDAV server is not accessible or event doesn't exist, which is expected in CI
        if status.is_success() {
            let update_response: serde_json::Value = response.json().await.unwrap();
            assert!(update_response["success"].as_bool().unwrap_or(false));
            assert_eq!(
                update_response["series_uid"].as_str().unwrap(),
                "test-series-uid"
            );
            println!("✓ Update event series test passed");
        } else if status == 404 {
            println!(
                "⚠ Update event series test skipped (event not found - expected for test data)"
            );
        } else {
            println!("⚠ Update event series test skipped (CalDAV server not accessible)");
        }
    }

    /// Test event series deletion endpoint
    #[tokio::test]
    async fn test_delete_event_series() {
        let server = TestServer::start().await;

        // First login to get a token
        let token = server.login().await;

        // Load password from env for CalDAV requests
        dotenvy::dotenv().ok();
        let password = "test".to_string();

        let delete_payload = json!({
            "series_uid": "test-series-to-delete",
            "calendar_path": "/calendars/test/default/",
            "event_href": "test-series.ics",
            "delete_scope": "all_in_series"
        });

        let response = server
            .client
            .post(&format!(
                "{}/api/calendar/events/series/delete",
                server.base_url
            ))
            .header("Authorization", format!("Bearer {}", token))
            .header("X-CalDAV-Password", password)
            .json(&delete_payload)
            .send()
            .await
            .unwrap();

        let status = response.status();
        println!("Delete series response status: {}", status);

        // Note: This might fail if CalDAV server is not accessible or event doesn't exist, which is expected in CI
        if status.is_success() {
            let delete_response: serde_json::Value = response.json().await.unwrap();
            assert!(delete_response["success"].as_bool().unwrap_or(false));
            println!("✓ Delete event series test passed");
        } else if status == 404 {
            println!(
                "⚠ Delete event series test skipped (event not found - expected for test data)"
            );
        } else {
            println!("⚠ Delete event series test skipped (CalDAV server not accessible)");
        }
    }

    /// Test invalid update scope
    #[tokio::test]
    async fn test_invalid_update_scope() {
        let server = TestServer::start().await;

        // First login to get a token
        let token = server.login().await;

        let invalid_payload = json!({
            "series_uid": "test-series-uid",
            "title": "Test Title",
            "description": "Test",
            "start_date": "2024-12-25",
            "start_time": "10:00",
            "end_date": "2024-12-25",
            "end_time": "11:00",
            "location": "Test",
            "all_day": false,
            "status": "confirmed",
            "class": "public",
            "organizer": "test@example.com",
            "attendees": "",
            "categories": "",
            "reminder": "none",
            "recurrence": "none",
            "recurrence_days": [false, false, false, false, false, false, false],
            "update_scope": "invalid_scope" // This should cause a 400 error
        });

        let response = server
            .client
            .post(&format!(
                "{}/api/calendar/events/series/update",
                server.base_url
            ))
            .header("Authorization", format!("Bearer {}", token))
            .json(&invalid_payload)
            .send()
            .await
            .unwrap();

        assert_eq!(
            response.status(),
            400,
            "Expected 400 for invalid update scope"
        );
        println!("✓ Invalid update scope test passed");
    }

    /// Test non-recurring event rejection in series endpoint
    #[tokio::test]
    async fn test_non_recurring_series_rejection() {
        let server = TestServer::start().await;

        // First login to get a token
        let token = server.login().await;

        let non_recurring_payload = json!({
            "title": "Non-recurring Event",
            "description": "This should be rejected",
            "start_date": "2024-12-25",
            "start_time": "10:00",
            "end_date": "2024-12-25",
            "end_time": "11:00",
            "location": "Test",
            "all_day": false,
            "status": "confirmed",
            "class": "public",
            "organizer": "test@example.com",
            "attendees": "",
            "categories": "",
            "reminder": "none",
            "recurrence": "none", // This should cause rejection
            "recurrence_days": [false, false, false, false, false, false, false]
        });

        let response = server
            .client
            .post(&format!(
                "{}/api/calendar/events/series/create",
                server.base_url
            ))
            .header("Authorization", format!("Bearer {}", token))
            .json(&non_recurring_payload)
            .send()
            .await
            .unwrap();

        assert_eq!(
            response.status(),
            400,
            "Expected 400 for non-recurring event in series endpoint"
        );
        println!("✓ Non-recurring series rejection test passed");
    }
}
