use crate::ai::{credits, litellm, model_router, orchestrator, plans, usage};
use crate::AppState;
use axum::{
    extract::{Path, Query, State},
    http::StatusCode,
    response::IntoResponse,
    routing::{get, post},
    Json, Router,
};
use contracts::auth_middleware::{require_admin, AuthUser};
use db::models::ai::{
    AiCreditTransactionRepository, AiFeatureCostRepository, AiPlanRepository,
    AiUsageLogRepository, UserAiSubscriptionRepository,
};
use serde::Deserialize;
use uuid::Uuid;

#[derive(Debug, Deserialize)]
struct AdminAiPrompt {
    context: String,
    #[serde(default)]
    model: Option<String>,
}

#[derive(Debug, Deserialize)]
struct AbuseCheckBody {
    text: String,
    #[serde(default)]
    model: Option<String>,
}

#[derive(Debug, Deserialize)]
struct UpdatePlanBody {
    user_id: Uuid,
    plan_code: String,
}

#[derive(Debug, Deserialize)]
struct AddCreditsBody {
    user_id: Uuid,
    credits: i32,
    #[serde(default = "default_credit_source")]
    source: String,
    description: Option<String>,
}

fn default_credit_source() -> String {
    "admin".to_string()
}

#[derive(Debug, Deserialize)]
struct ListLogsQuery {
    #[serde(default = "default_limit")]
    limit: i64,
    #[serde(default)]
    offset: i64,
}

fn default_limit() -> i64 {
    50
}

pub fn admin_ai_router() -> Router<AppState> {
    Router::new()
        .route("/support/reply", post(admin_support_reply))
        .route("/tickets/summary", post(admin_ticket_summary))
        .route("/verification/summary", post(admin_verification_summary))
        .route("/abuse/check", post(admin_abuse_check))
        .route("/users/{user_id}/plan", post(update_user_plan))
        .route("/users/{user_id}/credits", post(add_user_credits))
        .route("/users/{user_id}/usage", get(user_usage_logs))
        .route("/users/{user_id}/transactions", get(user_credit_transactions))
        .route("/plans", get(list_plans))
        .route("/features", get(list_features))
}

async fn admin_support_reply(
    State(state): State<AppState>,
    auth: AuthUser,
    Json(body): Json<AdminAiPrompt>,
) -> impl IntoResponse {
    if let Err(e) = require_admin(&auth) {
        return e.into_response();
    }

    let prompt = format!(
        "You are a Nxtgauge support assistant. Draft a helpful, professional reply to the following support context. \
         Keep it concise and empathetic.\n\nContext: {}\n\nReply:",
        body.context
    );

    admin_ai_call(
        &state,
        &auth,
        "admin_support_reply",
        &prompt,
        body.model.as_deref(),
    )
    .await
}

async fn admin_ticket_summary(
    State(state): State<AppState>,
    auth: AuthUser,
    Json(body): Json<AdminAiPrompt>,
) -> impl IntoResponse {
    if let Err(e) = require_admin(&auth) {
        return e.into_response();
    }

    let prompt = format!(
        "Summarize the following support tickets into key themes, open issues, and recommended next actions. \
         Use bullet points.\n\nContext: {}\n\nSummary:",
        body.context
    );

    admin_ai_call(
        &state,
        &auth,
        "admin_ticket_summary",
        &prompt,
        body.model.as_deref(),
    )
    .await
}

async fn admin_verification_summary(
    State(state): State<AppState>,
    auth: AuthUser,
    Json(body): Json<AdminAiPrompt>,
) -> impl IntoResponse {
    if let Err(e) = require_admin(&auth) {
        return e.into_response();
    }

    let prompt = format!(
        "You are a Nxtgauge verification reviewer. Given the following verification notes, \
         produce a short summary and a recommend action (approve, reject, request more info).\n\nContext: {}\n\nSummary and recommendation:",
        body.context
    );

    admin_ai_call(
        &state,
        &auth,
        "admin_verification_summary",
        &prompt,
        body.model.as_deref(),
    )
    .await
}

async fn admin_abuse_check(
    State(state): State<AppState>,
    auth: AuthUser,
    Json(body): Json<AbuseCheckBody>,
) -> impl IntoResponse {
    if let Err(e) = require_admin(&auth) {
        return e.into_response();
    }

    let prompt = format!(
        "You are a content moderation assistant. Review the following text and respond with ONLY one word: \
         SAFE or FLAGGED. Then add a one-sentence reason.\n\nText: {}\n\nVerdict:",
        body.text
    );

    admin_ai_call(
        &state,
        &auth,
        "abuse_check",
        &prompt,
        body.model.as_deref(),
    )
    .await
}

async fn admin_ai_call(
    state: &AppState,
    auth: &AuthUser,
    feature_code: &str,
    prompt: &str,
    requested_model: Option<&str>,
) -> axum::http::Response<axum::body::Body> {
    let role_code = auth.claims.active_role.clone();

    let (sub, plan) = match plans::ensure_free_subscription(
        &state.pool,
        auth.user_id,
        Some(&role_code),
    )
    .await
    {
        Ok(sp) => sp,
        Err(e) => return (e.status_code(), Json(e.error_body())).into_response(),
    };

    if !plans::is_feature_allowed(&plan, feature_code) {
        let e = plans::PlanError::FeatureNotAllowed {
            feature: feature_code.to_string(),
        };
        return (e.status_code(), Json(e.error_body())).into_response();
    }

    let feature_cost = match credits::get_feature_cost(&state.pool, feature_code).await {
        Ok(c) => c,
        Err(e) => return (e.status_code(), Json(e.error_body())).into_response(),
    };

    let model_alias = match model_router::resolve_model(&feature_cost, &plan, requested_model) {
        Ok(m) => m,
        Err(e) => return (e.status_code(), Json(e.error_body())).into_response(),
    };

    if credits::remaining_credits(&sub) < feature_cost.credit_cost {
        let e = credits::CreditError::InsufficientCredits;
        return (e.status_code(), Json(e.error_body())).into_response();
    }

    let client = match litellm::LiteLlmClient::new() {
        Ok(c) => c,
        Err(e) => {
            tracing::error!("LiteLLM client creation failed: {}", e);
            return (
                StatusCode::INTERNAL_SERVER_ERROR,
                Json(serde_json::json!({ "error": "AI service unavailable" })),
            )
                .into_response();
        }
    };

    let (text, usage, request_id) = match client
        .chat_completion_text(&model_alias, Some(&orchestrator::effective_system_prompt(None)), prompt, None)
        .await
    {
        Ok(r) => r,
        Err(e) => {
            tracing::error!("LiteLLM call failed: {}", e);
            return (
                StatusCode::BAD_GATEWAY,
                Json(serde_json::json!({ "error": "AI model call failed" })),
            )
                .into_response();
        }
    };

    if let Err(e) = credits::charge_feature(
        &state.pool,
        auth.user_id,
        Some(&role_code),
        feature_code,
        Some(&model_alias),
    )
    .await
    {
        tracing::error!(
            "Failed to charge admin AI feature {} for user {}: {}",
            feature_code,
            auth.user_id,
            e
        );
    }

    usage::log_usage(
        &state.pool,
        auth.user_id,
        Some(&role_code),
        feature_code,
        &model_alias,
        feature_cost.credit_cost,
        usage.prompt_tokens,
        usage.completion_tokens,
        usage.total_tokens,
        "success",
        request_id.as_deref(),
        None,
    )
    .await;

    let remaining_credits = credits::remaining_credits(&sub) - feature_cost.credit_cost;
    let remaining_daily = credits::remaining_daily_actions(&sub, &plan) - 1;

    (
        StatusCode::OK,
        Json(serde_json::json!({
            "message": text,
            "model": model_alias,
            "credits_charged": feature_cost.credit_cost,
            "remaining_credits": remaining_credits,
            "remaining_daily_actions": remaining_daily.max(0),
            "request_id": request_id,
        })),
    )
        .into_response()
}

async fn update_user_plan(
    State(state): State<AppState>,
    auth: AuthUser,
    Json(body): Json<UpdatePlanBody>,
) -> impl IntoResponse {
    if let Err(e) = require_admin(&auth) {
        return e.into_response();
    }

    let plan = match AiPlanRepository::get_by_code(&state.pool, &body.plan_code).await {
        Ok(Some(p)) => p,
        Ok(None) => {
            return (
                StatusCode::BAD_REQUEST,
                Json(serde_json::json!({ "error": "Plan not found" })),
            )
                .into_response();
        }
        Err(e) => {
            tracing::error!("Database error fetching plan: {}", e);
            return (
                StatusCode::INTERNAL_SERVER_ERROR,
                Json(serde_json::json!({ "error": "Database error" })),
            )
                .into_response();
        }
    };

    let (period_start, period_end) = plans::current_monthly_period(chrono::Utc::now());

    let sub = match UserAiSubscriptionRepository::update_plan(
        &state.pool,
        body.user_id,
        plan.id,
        plan.monthly_credits,
        period_start,
        period_end,
    )
    .await
    {
        Ok(s) => s,
        Err(e) => {
            tracing::error!("Failed to update plan for user {}: {}", body.user_id, e);
            return (
                StatusCode::INTERNAL_SERVER_ERROR,
                Json(serde_json::json!({ "error": "Failed to update plan" })),
            )
                .into_response();
        }
    };

    (
        StatusCode::OK,
        Json(serde_json::json!({
            "user_id": body.user_id,
            "plan_code": plan.code,
            "plan_name": plan.name,
            "monthly_credits_total": sub.monthly_credits_total,
            "current_period_start": sub.current_period_start,
            "current_period_end": sub.current_period_end,
        })),
    )
        .into_response()
}

async fn add_user_credits(
    State(state): State<AppState>,
    auth: AuthUser,
    Json(body): Json<AddCreditsBody>,
) -> impl IntoResponse {
    if let Err(e) = require_admin(&auth) {
        return e.into_response();
    }

    if body.credits <= 0 {
        return (
            StatusCode::BAD_REQUEST,
            Json(serde_json::json!({ "error": "Credits must be positive" })),
        )
            .into_response();
    }

    let sub = match UserAiSubscriptionRepository::get_by_user_id(&state.pool, body.user_id).await {
        Ok(Some(s)) => s,
        Ok(None) => {
            return (
                StatusCode::BAD_REQUEST,
                Json(serde_json::json!({ "error": "User has no AI subscription" })),
            )
                .into_response();
        }
        Err(e) => {
            tracing::error!("Database error fetching subscription: {}", e);
            return (
                StatusCode::INTERNAL_SERVER_ERROR,
                Json(serde_json::json!({ "error": "Database error" })),
            )
                .into_response();
        }
    };

    if let Err(e) =
        UserAiSubscriptionRepository::add_purchased_credits(&state.pool, body.user_id, body.credits)
            .await
    {
        tracing::error!("Failed to add credits for user {}: {}", body.user_id, e);
        return (
            StatusCode::INTERNAL_SERVER_ERROR,
            Json(serde_json::json!({ "error": "Failed to add credits" })),
        )
            .into_response();
    }

    let balance_after = credits::remaining_credits(&sub) + body.credits;
    if let Err(e) = AiCreditTransactionRepository::create(
        &state.pool,
        body.user_id,
        "credit",
        &body.source,
        body.credits,
        balance_after,
        None,
        body.description.as_deref(),
    )
    .await
    {
        tracing::error!("Failed to log credit transaction for user {}: {}", body.user_id, e);
    }

    (
        StatusCode::OK,
        Json(serde_json::json!({
            "user_id": body.user_id,
            "credits_added": body.credits,
            "balance_after": balance_after,
        })),
    )
        .into_response()
}

async fn user_usage_logs(
    State(state): State<AppState>,
    auth: AuthUser,
    Path(user_id): Path<Uuid>,
    Query(q): Query<ListLogsQuery>,
) -> impl IntoResponse {
    if let Err(e) = require_admin(&auth) {
        return e.into_response();
    }

    match AiUsageLogRepository::list_by_user(&state.pool, user_id, q.limit, q.offset).await {
        Ok(rows) => (StatusCode::OK, Json(serde_json::json!({ "logs": rows }))).into_response(),
        Err(e) => {
            tracing::error!("Failed to fetch usage logs for {}: {}", user_id, e);
            (
                StatusCode::INTERNAL_SERVER_ERROR,
                Json(serde_json::json!({ "error": "Database error" })),
            )
                .into_response()
        }
    }
}

async fn user_credit_transactions(
    State(state): State<AppState>,
    auth: AuthUser,
    Path(user_id): Path<Uuid>,
    Query(q): Query<ListLogsQuery>,
) -> impl IntoResponse {
    if let Err(e) = require_admin(&auth) {
        return e.into_response();
    }

    match AiCreditTransactionRepository::list_by_user(&state.pool, user_id, q.limit, q.offset)
        .await
    {
        Ok(rows) => (
            StatusCode::OK,
            Json(serde_json::json!({ "transactions": rows })),
        )
            .into_response(),
        Err(e) => {
            tracing::error!("Failed to fetch credit transactions for {}: {}", user_id, e);
            (
                StatusCode::INTERNAL_SERVER_ERROR,
                Json(serde_json::json!({ "error": "Database error" })),
            )
                .into_response()
        }
    }
}

async fn list_plans(State(state): State<AppState>, auth: AuthUser) -> impl IntoResponse {
    if let Err(e) = require_admin(&auth) {
        return e.into_response();
    }

    match AiPlanRepository::list_active(&state.pool).await {
        Ok(rows) => (StatusCode::OK, Json(serde_json::json!({ "plans": rows }))).into_response(),
        Err(e) => {
            tracing::error!("Failed to list plans: {}", e);
            (
                StatusCode::INTERNAL_SERVER_ERROR,
                Json(serde_json::json!({ "error": "Database error" })),
            )
                .into_response()
        }
    }
}

async fn list_features(State(state): State<AppState>, auth: AuthUser) -> impl IntoResponse {
    if let Err(e) = require_admin(&auth) {
        return e.into_response();
    }

    match AiFeatureCostRepository::list_active(&state.pool).await {
        Ok(rows) => (
            StatusCode::OK,
            Json(serde_json::json!({ "features": rows })),
        )
            .into_response(),
        Err(e) => {
            tracing::error!("Failed to list features: {}", e);
            (
                StatusCode::INTERNAL_SERVER_ERROR,
                Json(serde_json::json!({ "error": "Database error" })),
            )
                .into_response()
        }
    }
}
