use std::sync::Arc;

use axum::{
    extract::{Path, Query, State},
    routing::{get, patch, post},
    Json, Router,
};
use serde::{Deserialize, Serialize};
use uuid::Uuid;

use crate::middleware::auth::SuperAdmin;
use crate::models::error::{ApiError, ApiResult};
use crate::AppState;

pub fn router() -> Router<Arc<AppState>> {
    Router::new()
        .route("/stats", get(stats))
        .route("/licenses", get(list_licenses))
        .route("/licenses", post(create_license))
        .route("/licenses/{id}", get(get_license))
        .route("/licenses/{id}", patch(update_license))
        .route("/subscriptions", get(list_subscriptions))
        .route("/users", get(list_users))
        .route("/users/{id}", patch(update_user))
        .route("/servers", get(list_servers))
        .route("/health", get(health))
}

// ──────────────────────────────────────────────
// DTOs
// ──────────────────────────────────────────────

#[derive(Serialize)]
struct PlatformStats {
    total_licenses: i64,
    active_licenses: i64,
    total_users: i64,
    module_mrr: f64,
    servers_online: i64,
    new_signups_this_week: i64,
}

#[derive(Deserialize)]
struct PaginationParams {
    page: Option<i64>,
    per_page: Option<i64>,
    search: Option<String>,
    status: Option<String>,
}

#[derive(Serialize)]
struct PaginatedResponse<T: Serialize> {
    data: Vec<T>,
    total: i64,
    page: i64,
    per_page: i64,
}

#[derive(Serialize, sqlx::FromRow)]
struct LicenseListItem {
    id: Uuid,
    license_key: String,
    status: String,
    owner_email: String,
    owner_username: String,
    server_name: Option<String>,
    modules_enabled: Option<Vec<String>>,
    webstore_active: bool,
    created_at: chrono::DateTime<chrono::Utc>,
    expires_at: Option<chrono::DateTime<chrono::Utc>>,
}

#[derive(Serialize)]
struct LicenseDetail {
    license: LicenseListItem,
    team_count: i64,
    wipe_count: i64,
    server_connection: Option<ServerConnectionInfo>,
}

#[derive(Serialize, sqlx::FromRow)]
struct ServerConnectionInfo {
    connection_type: String,
    connection_status: String,
    server_ip: Option<String>,
    game_port: Option<i32>,
    plugin_last_seen: Option<chrono::DateTime<chrono::Utc>>,
    companion_last_seen: Option<chrono::DateTime<chrono::Utc>>,
}

#[derive(Deserialize)]
struct CreateLicenseRequest {
    owner_email: String,
    license_key: Option<String>,
}

#[derive(Deserialize)]
struct UpdateLicenseRequest {
    status: Option<String>,
    expires_at: Option<String>,
}

#[derive(Serialize, sqlx::FromRow)]
struct UserListItem {
    id: Uuid,
    email: String,
    username: String,
    is_super_admin: bool,
    email_verified: bool,
    license_count: i64,
    created_at: chrono::DateTime<chrono::Utc>,
    last_login_at: Option<chrono::DateTime<chrono::Utc>>,
}

#[derive(Deserialize)]
struct UpdateUserRequest {
    is_super_admin: Option<bool>,
    disabled: Option<bool>,
}

#[derive(Serialize, sqlx::FromRow)]
struct SubscriptionListItem {
    owner_email: String,
    owner_username: String,
    license_id: Uuid,
    module_name: String,
}

#[derive(Serialize)]
struct SubscriptionSummary {
    subscriptions: Vec<SubscriptionListItem>,
    module_counts: Vec<ModuleCount>,
    total_subscribers: i64,
}

#[derive(Serialize, sqlx::FromRow)]
struct ModuleCount {
    module_name: String,
    subscriber_count: i64,
}

#[derive(Serialize, sqlx::FromRow)]
struct ServerFleetItem {
    license_id: Uuid,
    owner_email: String,
    server_name: Option<String>,
    connection_type: String,
    connection_status: String,
    server_ip: Option<String>,
    game_port: Option<i32>,
    plugin_last_seen: Option<chrono::DateTime<chrono::Utc>>,
    companion_last_seen: Option<chrono::DateTime<chrono::Utc>>,
}

#[derive(Serialize)]
struct HealthInfo {
    db_pool_size: u32,
    nats_connected: bool,
    table_counts: Vec<TableCount>,
}

#[derive(Serialize)]
struct TableCount {
    table_name: String,
    row_count: i64,
}

// ──────────────────────────────────────────────
// Handlers
// ──────────────────────────────────────────────

/// GET /api/admin/stats — KPI summary
async fn stats(
    _admin: SuperAdmin,
    State(state): State<Arc<AppState>>,
) -> ApiResult<Json<PlatformStats>> {
    let total_licenses: (i64,) = sqlx::query_as("SELECT COUNT(*) FROM licenses")
        .fetch_one(&state.db).await.map_err(|e| ApiError::Internal(e.to_string()))?;

    let active_licenses: (i64,) = sqlx::query_as(
        "SELECT COUNT(*) FROM licenses WHERE status = 'active'"
    ).fetch_one(&state.db).await.map_err(|e| ApiError::Internal(e.to_string()))?;

    let total_users: (i64,) = sqlx::query_as("SELECT COUNT(*) FROM users")
        .fetch_one(&state.db).await.map_err(|e| ApiError::Internal(e.to_string()))?;

    // MRR from webstore subscriptions (count active webstore licenses * $10/mo)
    let webstore_count: (i64,) = sqlx::query_as(
        "SELECT COUNT(*) FROM licenses WHERE webstore_active = true"
    ).fetch_one(&state.db).await.map_err(|e| ApiError::Internal(e.to_string()))?;
    let module_mrr = webstore_count.0 as f64 * 10.0;

    let servers_online: (i64,) = sqlx::query_as(
        "SELECT COUNT(*) FROM server_connections WHERE connection_status = 'connected'"
    ).fetch_one(&state.db).await.map_err(|e| ApiError::Internal(e.to_string()))?;

    let new_signups: (i64,) = sqlx::query_as(
        "SELECT COUNT(*) FROM users WHERE created_at >= NOW() - INTERVAL '7 days'"
    ).fetch_one(&state.db).await.map_err(|e| ApiError::Internal(e.to_string()))?;

    Ok(Json(PlatformStats {
        total_licenses: total_licenses.0,
        active_licenses: active_licenses.0,
        total_users: total_users.0,
        module_mrr,
        servers_online: servers_online.0,
        new_signups_this_week: new_signups.0,
    }))
}

/// GET /api/admin/licenses — paginated, filterable
async fn list_licenses(
    _admin: SuperAdmin,
    State(state): State<Arc<AppState>>,
    Query(params): Query<PaginationParams>,
) -> ApiResult<Json<PaginatedResponse<LicenseListItem>>> {
    let page = params.page.unwrap_or(1).max(1);
    let per_page = params.per_page.unwrap_or(25).min(100);
    let offset = (page - 1) * per_page;

    let search = params.search.unwrap_or_default();
    let status_filter = params.status.unwrap_or_default();

    let licenses = sqlx::query_as::<_, LicenseListItem>(
        "SELECT l.id, l.license_key, l.status, u.email AS owner_email, \
         u.username AS owner_username, l.server_name, l.modules_enabled, \
         l.webstore_active, l.created_at, l.expires_at \
         FROM licenses l \
         JOIN users u ON u.id = l.owner_user_id \
         WHERE ($1 = '' OR l.license_key ILIKE '%' || $1 || '%' OR u.email ILIKE '%' || $1 || '%') \
         AND ($2 = '' OR l.status = $2) \
         ORDER BY l.created_at DESC \
         LIMIT $3 OFFSET $4"
    )
    .bind(&search)
    .bind(&status_filter)
    .bind(per_page)
    .bind(offset)
    .fetch_all(&state.db)
    .await
    .map_err(|e| ApiError::Internal(e.to_string()))?;

    let total: (i64,) = sqlx::query_as(
        "SELECT COUNT(*) FROM licenses l \
         JOIN users u ON u.id = l.owner_user_id \
         WHERE ($1 = '' OR l.license_key ILIKE '%' || $1 || '%' OR u.email ILIKE '%' || $1 || '%') \
         AND ($2 = '' OR l.status = $2)"
    )
    .bind(&search)
    .bind(&status_filter)
    .fetch_one(&state.db)
    .await
    .map_err(|e| ApiError::Internal(e.to_string()))?;

    Ok(Json(PaginatedResponse {
        data: licenses,
        total: total.0,
        page,
        per_page,
    }))
}

/// GET /api/admin/licenses/:id — detail view
async fn get_license(
    _admin: SuperAdmin,
    State(state): State<Arc<AppState>>,
    Path(id): Path<Uuid>,
) -> ApiResult<Json<LicenseDetail>> {
    let license = sqlx::query_as::<_, LicenseListItem>(
        "SELECT l.id, l.license_key, l.status, u.email AS owner_email, \
         u.username AS owner_username, l.server_name, l.modules_enabled, \
         l.webstore_active, l.created_at, l.expires_at \
         FROM licenses l \
         JOIN users u ON u.id = l.owner_user_id \
         WHERE l.id = $1"
    )
    .bind(id)
    .fetch_optional(&state.db)
    .await
    .map_err(|e| ApiError::Internal(e.to_string()))?
    .ok_or(ApiError::NotFound("License not found".to_string()))?;

    let team_count: (i64,) = sqlx::query_as(
        "SELECT COUNT(*) FROM team_members WHERE license_id = $1"
    )
    .bind(id)
    .fetch_one(&state.db)
    .await
    .map_err(|e| ApiError::Internal(e.to_string()))?;

    let wipe_count: (i64,) = sqlx::query_as(
        "SELECT COUNT(*) FROM wipe_history WHERE license_id = $1"
    )
    .bind(id)
    .fetch_one(&state.db)
    .await
    .map_err(|e| ApiError::Internal(e.to_string()))?;

    let server_connection = sqlx::query_as::<_, ServerConnectionInfo>(
        "SELECT connection_type, connection_status, server_ip, game_port, \
         plugin_last_seen, companion_last_seen \
         FROM server_connections WHERE license_id = $1"
    )
    .bind(id)
    .fetch_optional(&state.db)
    .await
    .map_err(|e| ApiError::Internal(e.to_string()))?;

    Ok(Json(LicenseDetail {
        license,
        team_count: team_count.0,
        wipe_count: wipe_count.0,
        server_connection,
    }))
}

/// POST /api/admin/licenses — manual generation
async fn create_license(
    _admin: SuperAdmin,
    State(state): State<Arc<AppState>>,
    Json(body): Json<CreateLicenseRequest>,
) -> ApiResult<Json<serde_json::Value>> {
    // Find user by email
    let user = crate::db::users::get_user_by_email(&state.db, &body.owner_email)
        .await
        .map_err(|e| ApiError::Internal(e.to_string()))?
        .ok_or(ApiError::NotFound("User not found".to_string()))?;

    let license_key = body.license_key.unwrap_or_else(|| {
        format!("CORROSION-{}", crate::services::encryption::generate_token(8).to_uppercase())
    });

    let id = crate::db::licenses::create_license(&state.db, &license_key, user.id)
        .await
        .map_err(|e| ApiError::Internal(e.to_string()))?;

    Ok(Json(serde_json::json!({
        "id": id,
        "license_key": license_key,
    })))
}

/// PATCH /api/admin/licenses/:id — revoke/activate
async fn update_license(
    _admin: SuperAdmin,
    State(state): State<Arc<AppState>>,
    Path(id): Path<Uuid>,
    Json(body): Json<UpdateLicenseRequest>,
) -> ApiResult<Json<serde_json::Value>> {
    if let Some(status) = &body.status {
        let valid = ["active", "suspended", "expired", "revoked"];
        if !valid.contains(&status.as_str()) {
            return Err(ApiError::BadRequest(format!("Invalid status: {status}")));
        }
        crate::db::licenses::update_license_status(&state.db, id, status)
            .await
            .map_err(|e| ApiError::Internal(e.to_string()))?;
    }

    if let Some(expires_at) = &body.expires_at {
        let parsed = chrono::DateTime::parse_from_rfc3339(expires_at)
            .map_err(|_| ApiError::BadRequest("Invalid date format, use RFC3339".to_string()))?;
        sqlx::query("UPDATE licenses SET expires_at = $1 WHERE id = $2")
            .bind(parsed)
            .bind(id)
            .execute(&state.db)
            .await
            .map_err(|e| ApiError::Internal(e.to_string()))?;
    }

    Ok(Json(serde_json::json!({ "updated": true })))
}

/// GET /api/admin/subscriptions — module subscription list
async fn list_subscriptions(
    _admin: SuperAdmin,
    State(state): State<Arc<AppState>>,
) -> ApiResult<Json<SubscriptionSummary>> {
    // Unnest modules_enabled array to get per-module subscription data
    let subscriptions = sqlx::query_as::<_, SubscriptionListItem>(
        "SELECT u.email AS owner_email, u.username AS owner_username, \
         l.id AS license_id, unnest(l.modules_enabled) AS module_name \
         FROM licenses l \
         JOIN users u ON u.id = l.owner_user_id \
         WHERE l.status = 'active' AND array_length(l.modules_enabled, 1) > 0 \
         ORDER BY u.email"
    )
    .fetch_all(&state.db)
    .await
    .map_err(|e| ApiError::Internal(e.to_string()))?;

    let module_counts = sqlx::query_as::<_, ModuleCount>(
        "SELECT m AS module_name, COUNT(*) AS subscriber_count \
         FROM licenses, unnest(modules_enabled) AS m \
         WHERE status = 'active' AND array_length(modules_enabled, 1) > 0 \
         GROUP BY m ORDER BY subscriber_count DESC"
    )
    .fetch_all(&state.db)
    .await
    .map_err(|e| ApiError::Internal(e.to_string()))?;

    let total: i64 = module_counts.iter().map(|m| m.subscriber_count).sum();

    Ok(Json(SubscriptionSummary {
        subscriptions,
        module_counts,
        total_subscribers: total,
    }))
}

/// GET /api/admin/users — paginated, filterable
async fn list_users(
    _admin: SuperAdmin,
    State(state): State<Arc<AppState>>,
    Query(params): Query<PaginationParams>,
) -> ApiResult<Json<PaginatedResponse<UserListItem>>> {
    let page = params.page.unwrap_or(1).max(1);
    let per_page = params.per_page.unwrap_or(25).min(100);
    let offset = (page - 1) * per_page;
    let search = params.search.unwrap_or_default();

    let users = sqlx::query_as::<_, UserListItem>(
        "SELECT u.id, u.email, u.username, u.is_super_admin, u.email_verified, \
         COUNT(l.id) AS license_count, u.created_at, u.last_login_at \
         FROM users u \
         LEFT JOIN licenses l ON l.owner_user_id = u.id \
         WHERE ($1 = '' OR u.email ILIKE '%' || $1 || '%' OR u.username ILIKE '%' || $1 || '%') \
         GROUP BY u.id \
         ORDER BY u.created_at DESC \
         LIMIT $2 OFFSET $3"
    )
    .bind(&search)
    .bind(per_page)
    .bind(offset)
    .fetch_all(&state.db)
    .await
    .map_err(|e| ApiError::Internal(e.to_string()))?;

    let total: (i64,) = sqlx::query_as(
        "SELECT COUNT(*) FROM users \
         WHERE ($1 = '' OR email ILIKE '%' || $1 || '%' OR username ILIKE '%' || $1 || '%')"
    )
    .bind(&search)
    .fetch_one(&state.db)
    .await
    .map_err(|e| ApiError::Internal(e.to_string()))?;

    Ok(Json(PaginatedResponse {
        data: users,
        total: total.0,
        page,
        per_page,
    }))
}

/// PATCH /api/admin/users/:id — disable/enable/set-admin
async fn update_user(
    _admin: SuperAdmin,
    State(state): State<Arc<AppState>>,
    Path(id): Path<Uuid>,
    Json(body): Json<UpdateUserRequest>,
) -> ApiResult<Json<serde_json::Value>> {
    // Verify user exists
    let _user = crate::db::users::get_user_by_id(&state.db, id)
        .await
        .map_err(|e| ApiError::Internal(e.to_string()))?
        .ok_or(ApiError::NotFound("User not found".to_string()))?;

    if let Some(is_admin) = body.is_super_admin {
        sqlx::query("UPDATE users SET is_super_admin = $1 WHERE id = $2")
            .bind(is_admin)
            .bind(id)
            .execute(&state.db)
            .await
            .map_err(|e| ApiError::Internal(e.to_string()))?;
    }

    // "Disabled" means we revoke all their licenses
    if body.disabled == Some(true) {
        sqlx::query("UPDATE licenses SET status = 'suspended' WHERE owner_user_id = $1")
            .bind(id)
            .execute(&state.db)
            .await
            .map_err(|e| ApiError::Internal(e.to_string()))?;
    }

    Ok(Json(serde_json::json!({ "updated": true })))
}

/// GET /api/admin/servers — fleet overview
async fn list_servers(
    _admin: SuperAdmin,
    State(state): State<Arc<AppState>>,
    Query(params): Query<PaginationParams>,
) -> ApiResult<Json<Vec<ServerFleetItem>>> {
    let status_filter = params.status.unwrap_or_default();

    let servers = sqlx::query_as::<_, ServerFleetItem>(
        "SELECT sc.license_id, u.email AS owner_email, \
         cfg.server_name, sc.connection_type, sc.connection_status, \
         sc.server_ip, sc.game_port, sc.plugin_last_seen, sc.companion_last_seen \
         FROM server_connections sc \
         JOIN licenses l ON l.id = sc.license_id \
         JOIN users u ON u.id = l.owner_user_id \
         LEFT JOIN server_config cfg ON cfg.license_id = sc.license_id \
         WHERE ($1 = '' OR sc.connection_status = $1) \
         ORDER BY sc.connection_status ASC, u.email ASC"
    )
    .bind(&status_filter)
    .fetch_all(&state.db)
    .await
    .map_err(|e| ApiError::Internal(e.to_string()))?;

    Ok(Json(servers))
}

/// GET /api/admin/health — system metrics
async fn health(
    _admin: SuperAdmin,
    State(state): State<Arc<AppState>>,
) -> ApiResult<Json<HealthInfo>> {
    let tables = vec![
        "users", "licenses", "server_connections", "wipe_history",
        "chat_logs", "plugin_registry", "webstore_transactions",
    ];

    let mut table_counts = Vec::new();
    for table in tables {
        let count: (i64,) = sqlx::query_as(&format!("SELECT COUNT(*) FROM {table}"))
            .fetch_one(&state.db)
            .await
            .unwrap_or((0,));
        table_counts.push(TableCount {
            table_name: table.to_string(),
            row_count: count.0,
        });
    }

    Ok(Json(HealthInfo {
        db_pool_size: state.config.database_max_connections,
        nats_connected: state.nats.is_some(),
        table_counts,
    }))
}
