use std::sync::Arc;

use axum::{
    extract::{Request, State},
    response::Json,
};
use ontoref_derive::onto_api;
use serde::{Deserialize, Serialize};
use tracing::info;

use crate::error::{auth, ControlCenterError, Result};
use crate::middleware::RequestExt;
use crate::models::{ClientInfo, LoginRequest, LogoutRequest, RefreshTokenRequest, TokenResponse};
use crate::services::AuthService;
use crate::AppState;

/// Login endpoint
#[onto_api(
    method = "POST",
    path = "/auth/login",
    description = "Authenticate and obtain JWT tokens",
    auth = "none",
    actors = "developer, agent",
    tags = "auth",
    feature = ""
)]
pub async fn login(
    State(app_state): State<Arc<AppState>>,
    Json(request): Json<LoginRequest>,
) -> Result<Json<ApiResponse<TokenResponse>>> {
    // Extract client info from headers (simplified - you might want more
    // sophisticated detection)
    let client_info = Some(ClientInfo {
        user_agent: None, // Could extract from headers
        ip_address: None, // Could extract from connection info
        device_type: None,
    });

    let token_response = app_state.auth_service.login(request, client_info).await?;

    Ok(Json(ApiResponse::success(token_response)))
}

/// Refresh token endpoint
#[onto_api(
    method = "POST",
    path = "/auth/refresh",
    description = "Refresh JWT access token",
    auth = "none",
    actors = "developer, agent",
    tags = "auth",
    feature = ""
)]
pub async fn refresh_token(
    State(app_state): State<Arc<AppState>>,
    Json(request): Json<RefreshTokenRequest>,
) -> Result<Json<ApiResponse<TokenResponse>>> {
    let token_response = app_state.auth_service.refresh_token(request).await?;

    Ok(Json(ApiResponse::success(token_response)))
}

/// Logout endpoint
#[onto_api(
    method = "POST",
    path = "/auth/logout",
    description = "Invalidate current session and logout",
    auth = "bearer",
    actors = "developer, agent",
    tags = "auth",
    feature = ""
)]
pub async fn logout(
    State(app_state): State<Arc<AppState>>,
    Json(logout_request): Json<LogoutRequest>,
) -> Result<Json<ApiResponse<String>>> {
    // For now, we'll extract user_id from the token in logout_request
    // This would need to be updated to work with proper authentication middleware
    let user_id = uuid::Uuid::new_v4(); // Placeholder - this should come from JWT validation

    app_state
        .auth_service
        .logout(logout_request, user_id)
        .await?;

    Ok(Json(ApiResponse::success(
        "Logged out successfully".to_string(),
    )))
}

/// Verify token endpoint
pub async fn verify_token(
    State(app_state): State<Arc<AppState>>,
    request: Request,
) -> Result<Json<ApiResponse<TokenVerificationResponse>>> {
    let user_context = request.require_user_context()?;

    // Get user details
    let user = app_state
        .user_service
        .get_user_response_by_id(user_context.user_id)
        .await?;

    let response = TokenVerificationResponse {
        valid: true,
        user,
        session_id: user_context.session_id,
        roles: user_context.roles.clone(),
    };

    Ok(Json(ApiResponse::success(response)))
}

/// Get current user sessions
pub async fn get_sessions(
    State(app_state): State<Arc<AppState>>,
    request: Request,
) -> Result<Json<ApiResponse<Vec<crate::models::SessionResponse>>>> {
    let user_context = request.require_user_context()?;

    let sessions = app_state
        .auth_service
        .get_user_sessions(user_context.user_id)
        .await?;

    let session_responses = sessions
        .into_iter()
        .map(crate::models::SessionResponse::from)
        .collect();

    Ok(Json(ApiResponse::success(session_responses)))
}

/// Invalidate all sessions (force logout everywhere)
pub async fn invalidate_all_sessions(
    State(app_state): State<Arc<AppState>>,
    request: Request,
) -> Result<Json<ApiResponse<String>>> {
    let user_context = request.require_user_context()?;

    let logout_request = LogoutRequest {
        session_id: None,
        all_sessions: Some(true),
    };

    app_state
        .auth_service
        .logout(logout_request, user_context.user_id)
        .await?;

    Ok(Json(ApiResponse::success(
        "All sessions invalidated successfully".to_string(),
    )))
}

/// Health check endpoint (no auth required)
#[onto_api(
    method = "GET",
    path = "/health",
    description = "Service health check",
    auth = "none",
    actors = "developer, agent, ci",
    tags = "health",
    feature = ""
)]
pub async fn health_check() -> Json<ApiResponse<HealthCheckResponse>> {
    Json(ApiResponse::success(HealthCheckResponse {
        status: "healthy".to_string(),
        timestamp: chrono::Utc::now(),
        version: env!("CARGO_PKG_VERSION").to_string(),
        service: "control-center".to_string(),
    }))
}

/// Token verification response
#[derive(Debug, Serialize)]
pub struct TokenVerificationResponse {
    pub valid: bool,
    pub user: crate::models::UserResponse,
    pub session_id: uuid::Uuid,
    pub roles: Vec<String>,
}

/// Health check response
#[derive(Debug, Serialize)]
pub struct HealthCheckResponse {
    pub status: String,
    pub timestamp: chrono::DateTime<chrono::Utc>,
    pub version: String,
    pub service: String,
}

/// Generic API response wrapper
#[derive(Debug, Serialize)]
pub struct ApiResponse<T> {
    pub success: bool,
    pub data: Option<T>,
    pub message: Option<String>,
    pub timestamp: chrono::DateTime<chrono::Utc>,
}

impl<T> ApiResponse<T> {
    pub fn success(data: T) -> Self {
        Self {
            success: true,
            data: Some(data),
            message: None,
            timestamp: chrono::Utc::now(),
        }
    }

    pub fn success_with_message(data: T, message: String) -> Self {
        Self {
            success: true,
            data: Some(data),
            message: Some(message),
            timestamp: chrono::Utc::now(),
        }
    }

    pub fn error(message: String) -> ApiResponse<()> {
        ApiResponse {
            success: false,
            data: None,
            message: Some(message),
            timestamp: chrono::Utc::now(),
        }
    }
}

/// Password change request
#[derive(Debug, Deserialize)]
pub struct ChangePasswordRequest {
    pub current_password: String,
    pub new_password: String,
}

/// Change password endpoint (authenticated users only)
pub async fn change_password(
    State(app_state): State<Arc<AppState>>,
    Json(password_request): Json<ChangePasswordRequest>,
    request: Request,
) -> Result<Json<ApiResponse<String>>> {
    let user_context = request.require_user_context()?;

    // Get current user
    let user = app_state
        .user_service
        .get_by_id(user_context.user_id)
        .await?;

    // Verify current password
    if !app_state
        .auth_service
        .verify_password(&password_request.current_password, &user.password_hash)?
    {
        return Err(ControlCenterError::Auth(auth::AuthError::Authentication(
            "Current password is incorrect".to_string(),
        )));
    }

    // Hash new password
    let new_password_hash = AuthService::hash_password(&password_request.new_password)?;

    // Update user password
    let update_request = crate::models::UpdateUserRequest {
        password: Some(new_password_hash),
        ..Default::default()
    };

    app_state
        .user_service
        .update_user(user_context.user_id, update_request)
        .await?;

    // Optionally invalidate all other sessions (force re-login)
    let logout_request = LogoutRequest {
        session_id: None,
        all_sessions: Some(true),
    };
    app_state
        .auth_service
        .logout(logout_request, user_context.user_id)
        .await?;

    info!("Password changed for user: {}", user.username);

    Ok(Json(ApiResponse::success(
        "Password changed successfully. Please log in again.".to_string(),
    )))
}

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

    #[test]
    fn test_api_response_creation() {
        let success_response = ApiResponse::success("test data".to_string());
        assert!(success_response.success);
        assert_eq!(success_response.data, Some("test data".to_string()));
        assert!(success_response.message.is_none());

        let success_with_message = ApiResponse::success_with_message(
            "test data".to_string(),
            "Operation completed".to_string(),
        );
        assert!(success_with_message.success);
        assert_eq!(success_with_message.data, Some("test data".to_string()));
        assert_eq!(
            success_with_message.message,
            Some("Operation completed".to_string())
        );

        let error_response = ApiResponse::<()>::error("Something went wrong".to_string());
        assert!(!error_response.success);
        assert!(error_response.data.is_none());
        assert_eq!(
            error_response.message,
            Some("Something went wrong".to_string())
        );
    }
}
