use axum::{
    extract::{Path, State},
    http::StatusCode,
    response::IntoResponse,
    routing::{get, post, put, delete},
    Json, Router,
};
use serde::{Deserialize, Serialize};
use std::sync::Arc;
use uuid::Uuid;

use crate::{
    middleware::jwt::Claims,
    models::error::ApiError,
    services::subscription_processor::{SubscriptionProcessor, SubscriptionDetails},
    AppState,
};

pub fn router() -> Router<Arc<AppState>> {
    Router::new()
        // Subscription management
        .route("/subscription", get(get_subscription_status))
        .route("/subscription/create", post(create_subscription))
        .route("/subscription/cancel", post(cancel_subscription))
        .route("/subscription/webhook", post(handle_subscription_webhook))
        // Store configuration
        .route("/config", get(get_store_config))
        .route("/config", put(update_store_config))
        // Store categories
        .route("/categories", get(list_categories))
        .route("/categories", post(create_category))
        .route("/categories/:id", put(update_category))
        .route("/categories/:id", delete(delete_category))
        // Store items
        .route("/items", get(list_items))
        .route("/items", post(create_item))
        .route("/items/:id", put(update_item))
        .route("/items/:id", delete(delete_item))
        // Transaction history
        .route("/transactions", get(list_transactions))
}

// ============================================================================
// SUBSCRIPTION MANAGEMENT
// ============================================================================

#[derive(Deserialize)]
struct CreateSubscriptionRequest {
    plan_id: String, // PayPal plan ID for $10/mo webstore feature
}

#[derive(Serialize)]
struct CreateSubscriptionResponse {
    approval_url: String,
}

/// Create a new PayPal subscription for webstore feature access
async fn create_subscription(
    State(state): State<Arc<AppState>>,
    claims: Claims,
    Json(req): Json<CreateSubscriptionRequest>,
) -> Result<impl IntoResponse, ApiError> {
    // Get license_id from JWT claims
    let license_id = claims.license_id.ok_or_else(|| {
        ApiError::Unauthorized("No license associated with this account".to_string())
    })?;

    // Check if subscription already exists
    let existing = sqlx::query!(
        "SELECT id FROM webstore_subscriptions WHERE license_id = $1 AND status = 'active'",
        license_id
    )
    .fetch_optional(&state.db)
    .await?;

    if existing.is_some() {
        return Err(ApiError::BadRequest(
            "You already have an active webstore subscription".to_string(),
        ));
    }

    // Get PayPal credentials from env
    let client_id = std::env::var("PAYPAL_CLIENT_ID")
        .map_err(|_| ApiError::Internal("PayPal credentials not configured".to_string()))?;
    let client_secret = std::env::var("PAYPAL_CLIENT_SECRET")
        .map_err(|_| ApiError::Internal("PayPal credentials not configured".to_string()))?;
    let sandbox_mode = std::env::var("PAYPAL_SANDBOX")
        .unwrap_or_else(|_| "true".to_string())
        .parse()
        .unwrap_or(true);

    let processor = SubscriptionProcessor::new(
        client_id,
        client_secret,
        sandbox_mode,
        state.db.clone(),
    );

    // Create PayPal subscription
    let approval_url = processor
        .create_webstore_subscription(license_id, &req.plan_id)
        .await?;

    Ok(Json(CreateSubscriptionResponse { approval_url }))
}

#[derive(Serialize)]
struct SubscriptionStatusResponse {
    active: bool,
    subscription: Option<SubscriptionDetails>,
}

/// Get current subscription status for the authenticated license
async fn get_subscription_status(
    State(state): State<Arc<AppState>>,
    claims: Claims,
) -> Result<impl IntoResponse, ApiError> {
    let license_id = claims.license_id.ok_or_else(|| {
        ApiError::Unauthorized("No license associated with this account".to_string())
    })?;

    let subscription = sqlx::query!(
        "SELECT paypal_subscription_id, status, current_period_start, current_period_end, cancelled_at
         FROM webstore_subscriptions
         WHERE license_id = $1
         ORDER BY created_at DESC
         LIMIT 1",
        license_id
    )
    .fetch_optional(&state.db)
    .await?;

    if let Some(sub) = subscription {
        let active = sub.status == "active";

        // Fetch full details from PayPal if active
        let details = if active {
            let client_id = std::env::var("PAYPAL_CLIENT_ID").unwrap_or_default();
            let client_secret = std::env::var("PAYPAL_CLIENT_SECRET").unwrap_or_default();
            let sandbox_mode = std::env::var("PAYPAL_SANDBOX")
                .unwrap_or_else(|_| "true".to_string())
                .parse()
                .unwrap_or(true);

            let processor = SubscriptionProcessor::new(
                client_id,
                client_secret,
                sandbox_mode,
                state.db.clone(),
            );

            processor
                .get_subscription_details(&sub.paypal_subscription_id)
                .await
                .ok()
        } else {
            None
        };

        Ok(Json(SubscriptionStatusResponse {
            active,
            subscription: details,
        }))
    } else {
        Ok(Json(SubscriptionStatusResponse {
            active: false,
            subscription: None,
        }))
    }
}

#[derive(Deserialize)]
struct CancelSubscriptionRequest {
    reason: String,
}

/// Cancel an active subscription
async fn cancel_subscription(
    State(state): State<Arc<AppState>>,
    claims: Claims,
    Json(req): Json<CancelSubscriptionRequest>,
) -> Result<impl IntoResponse, ApiError> {
    let license_id = claims.license_id.ok_or_else(|| {
        ApiError::Unauthorized("No license associated with this account".to_string())
    })?;

    let subscription = sqlx::query!(
        "SELECT paypal_subscription_id
         FROM webstore_subscriptions
         WHERE license_id = $1 AND status = 'active'
         LIMIT 1",
        license_id
    )
    .fetch_optional(&state.db)
    .await?;

    let subscription = subscription.ok_or_else(|| {
        ApiError::NotFound("No active subscription found".to_string())
    })?;

    let client_id = std::env::var("PAYPAL_CLIENT_ID")
        .map_err(|_| ApiError::Internal("PayPal credentials not configured".to_string()))?;
    let client_secret = std::env::var("PAYPAL_CLIENT_SECRET")
        .map_err(|_| ApiError::Internal("PayPal credentials not configured".to_string()))?;
    let sandbox_mode = std::env::var("PAYPAL_SANDBOX")
        .unwrap_or_else(|_| "true".to_string())
        .parse()
        .unwrap_or(true);

    let processor = SubscriptionProcessor::new(
        client_id,
        client_secret,
        sandbox_mode,
        state.db.clone(),
    );

    processor
        .cancel_subscription(&subscription.paypal_subscription_id, &req.reason)
        .await?;

    // Update local status
    sqlx::query!(
        "UPDATE webstore_subscriptions SET status = 'cancelled', cancelled_at = NOW() WHERE paypal_subscription_id = $1",
        subscription.paypal_subscription_id
    )
    .execute(&state.db)
    .await?;

    Ok(StatusCode::NO_CONTENT)
}

/// Handle PayPal subscription webhooks
async fn handle_subscription_webhook(
    State(state): State<Arc<AppState>>,
    Json(event): Json<serde_json::Value>,
) -> Result<impl IntoResponse, ApiError> {
    // TODO: Verify webhook signature using PayPal webhook ID
    // For now, just log and process

    tracing::info!("Received subscription webhook: {:?}", event);

    let client_id = std::env::var("PAYPAL_CLIENT_ID").unwrap_or_default();
    let client_secret = std::env::var("PAYPAL_CLIENT_SECRET").unwrap_or_default();
    let sandbox_mode = std::env::var("PAYPAL_SANDBOX")
        .unwrap_or_else(|_| "true".to_string())
        .parse()
        .unwrap_or(true);

    let processor = SubscriptionProcessor::new(
        client_id,
        client_secret,
        sandbox_mode,
        state.db.clone(),
    );

    // Parse webhook event
    let webhook_event: crate::services::subscription_processor::SubscriptionWebhookEvent =
        serde_json::from_value(event)?;

    processor.process_subscription_webhook(webhook_event).await?;

    Ok(StatusCode::OK)
}

// ============================================================================
// STORE CONFIGURATION
// ============================================================================

#[derive(Serialize, Deserialize)]
struct StoreConfig {
    store_name: String,
    description: Option<String>,
    currency: Option<String>,
    paypal_client_id: Option<String>,
    sandbox_mode: Option<bool>,
    enabled: Option<bool>,
}

async fn get_store_config(
    State(state): State<Arc<AppState>>,
    claims: Claims,
) -> Result<impl IntoResponse, ApiError> {
    let license_id = claims.license_id.ok_or_else(|| {
        ApiError::Unauthorized("No license associated with this account".to_string())
    })?;

    let config = sqlx::query_as!(
        StoreConfig,
        "SELECT store_name, description, currency, paypal_client_id, sandbox_mode, enabled
         FROM store_config
         WHERE license_id = $1",
        license_id
    )
    .fetch_optional(&state.db)
    .await?;

    if let Some(config) = config {
        Ok(Json(config))
    } else {
        // Return default config
        Ok(Json(StoreConfig {
            store_name: "My Store".to_string(),
            description: None,
            currency: "USD".to_string(),
            paypal_client_id: None,
            sandbox_mode: true,
            enabled: false,
        }))
    }
}

async fn update_store_config(
    State(state): State<Arc<AppState>>,
    claims: Claims,
    Json(config): Json<StoreConfig>,
) -> Result<impl IntoResponse, ApiError> {
    let license_id = claims.license_id.ok_or_else(|| {
        ApiError::Unauthorized("No license associated with this account".to_string())
    })?;

    // Upsert store config
    sqlx::query!(
        "INSERT INTO store_config (license_id, store_name, description, currency, paypal_client_id, sandbox_mode, enabled)
         VALUES ($1, $2, $3, $4, $5, $6, $7)
         ON CONFLICT (license_id)
         DO UPDATE SET
            store_name = $2,
            description = $3,
            currency = $4,
            paypal_client_id = $5,
            sandbox_mode = $6,
            enabled = $7,
            updated_at = NOW()",
        license_id,
        config.store_name,
        config.description,
        config.currency,
        config.paypal_client_id,
        config.sandbox_mode,
        config.enabled
    )
    .execute(&state.db)
    .await?;

    Ok(StatusCode::NO_CONTENT)
}

// ============================================================================
// STORE CATEGORIES
// ============================================================================

#[derive(Serialize, Deserialize)]
struct StoreCategory {
    id: Uuid,
    name: String,
    slug: String,
    description: Option<String>,
    display_order: i32,
    visible: bool,
}

async fn list_categories(
    State(state): State<Arc<AppState>>,
    claims: Claims,
) -> Result<impl IntoResponse, ApiError> {
    let license_id = claims.license_id.ok_or_else(|| {
        ApiError::Unauthorized("No license associated with this account".to_string())
    })?;

    let categories = sqlx::query_as!(
        StoreCategory,
        "SELECT id, name, slug, description, display_order, visible
         FROM store_categories
         WHERE license_id = $1
         ORDER BY display_order, name",
        license_id
    )
    .fetch_all(&state.db)
    .await?;

    Ok(Json(categories))
}

#[derive(Deserialize)]
struct CreateCategoryRequest {
    name: String,
    slug: String,
    description: Option<String>,
    display_order: i32,
    visible: bool,
}

async fn create_category(
    State(state): State<Arc<AppState>>,
    claims: Claims,
    Json(req): Json<CreateCategoryRequest>,
) -> Result<impl IntoResponse, ApiError> {
    let license_id = claims.license_id.ok_or_else(|| {
        ApiError::Unauthorized("No license associated with this account".to_string())
    })?;

    let id = sqlx::query_scalar!(
        "INSERT INTO store_categories (license_id, name, slug, description, display_order, visible)
         VALUES ($1, $2, $3, $4, $5, $6)
         RETURNING id",
        license_id,
        req.name,
        req.slug,
        req.description,
        req.display_order,
        req.visible
    )
    .fetch_one(&state.db)
    .await?;

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

async fn update_category(
    State(state): State<Arc<AppState>>,
    claims: Claims,
    Path(id): Path<Uuid>,
    Json(req): Json<CreateCategoryRequest>,
) -> Result<impl IntoResponse, ApiError> {
    let license_id = claims.license_id.ok_or_else(|| {
        ApiError::Unauthorized("No license associated with this account".to_string())
    })?;

    let result = sqlx::query!(
        "UPDATE store_categories
         SET name = $1, slug = $2, description = $3, display_order = $4, visible = $5
         WHERE id = $6 AND license_id = $7",
        req.name,
        req.slug,
        req.description,
        req.display_order,
        req.visible,
        id,
        license_id
    )
    .execute(&state.db)
    .await?;

    if result.rows_affected() == 0 {
        return Err(ApiError::NotFound("Category not found".to_string()));
    }

    Ok(StatusCode::NO_CONTENT)
}

async fn delete_category(
    State(state): State<Arc<AppState>>,
    claims: Claims,
    Path(id): Path<Uuid>,
) -> Result<impl IntoResponse, ApiError> {
    let license_id = claims.license_id.ok_or_else(|| {
        ApiError::Unauthorized("No license associated with this account".to_string())
    })?;

    let result = sqlx::query!(
        "DELETE FROM store_categories WHERE id = $1 AND license_id = $2",
        id,
        license_id
    )
    .execute(&state.db)
    .await?;

    if result.rows_affected() == 0 {
        return Err(ApiError::NotFound("Category not found".to_string()));
    }

    Ok(StatusCode::NO_CONTENT)
}

// ============================================================================
// STORE ITEMS
// ============================================================================

#[derive(Serialize, Deserialize)]
struct StoreItem {
    id: Uuid,
    category_id: Option<Uuid>,
    name: String,
    description: Option<String>,
    price: rust_decimal::Decimal,
    image_url: Option<String>,
    item_type: String,
    delivery_commands: serde_json::Value,
    limit_per_player: Option<i32>,
    enabled: bool,
}

async fn list_items(
    State(state): State<Arc<AppState>>,
    claims: Claims,
) -> Result<impl IntoResponse, ApiError> {
    let license_id = claims.license_id.ok_or_else(|| {
        ApiError::Unauthorized("No license associated with this account".to_string())
    })?;

    let items = sqlx::query_as!(
        StoreItem,
        "SELECT id, category_id, name, description, price, image_url, item_type, delivery_commands, limit_per_player, enabled
         FROM store_items
         WHERE license_id = $1
         ORDER BY name",
        license_id
    )
    .fetch_all(&state.db)
    .await?;

    Ok(Json(items))
}

#[derive(Deserialize)]
struct CreateItemRequest {
    category_id: Option<Uuid>,
    name: String,
    description: Option<String>,
    price: rust_decimal::Decimal,
    image_url: Option<String>,
    item_type: String,
    delivery_commands: serde_json::Value,
    limit_per_player: Option<i32>,
    enabled: bool,
}

async fn create_item(
    State(state): State<Arc<AppState>>,
    claims: Claims,
    Json(req): Json<CreateItemRequest>,
) -> Result<impl IntoResponse, ApiError> {
    let license_id = claims.license_id.ok_or_else(|| {
        ApiError::Unauthorized("No license associated with this account".to_string())
    })?;

    let id = sqlx::query_scalar!(
        "INSERT INTO store_items (license_id, category_id, name, description, price, image_url, item_type, delivery_commands, limit_per_player, enabled)
         VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)
         RETURNING id",
        license_id,
        req.category_id,
        req.name,
        req.description,
        req.price,
        req.image_url,
        req.item_type,
        req.delivery_commands,
        req.limit_per_player,
        req.enabled
    )
    .fetch_one(&state.db)
    .await?;

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

async fn update_item(
    State(state): State<Arc<AppState>>,
    claims: Claims,
    Path(id): Path<Uuid>,
    Json(req): Json<CreateItemRequest>,
) -> Result<impl IntoResponse, ApiError> {
    let license_id = claims.license_id.ok_or_else(|| {
        ApiError::Unauthorized("No license associated with this account".to_string())
    })?;

    let result = sqlx::query!(
        "UPDATE store_items
         SET category_id = $1, name = $2, description = $3, price = $4, image_url = $5, item_type = $6, delivery_commands = $7, limit_per_player = $8, enabled = $9, updated_at = NOW()
         WHERE id = $10 AND license_id = $11",
        req.category_id,
        req.name,
        req.description,
        req.price,
        req.image_url,
        req.item_type,
        req.delivery_commands,
        req.limit_per_player,
        req.enabled,
        id,
        license_id
    )
    .execute(&state.db)
    .await?;

    if result.rows_affected() == 0 {
        return Err(ApiError::NotFound("Item not found".to_string()));
    }

    Ok(StatusCode::NO_CONTENT)
}

async fn delete_item(
    State(state): State<Arc<AppState>>,
    claims: Claims,
    Path(id): Path<Uuid>,
) -> Result<impl IntoResponse, ApiError> {
    let license_id = claims.license_id.ok_or_else(|| {
        ApiError::Unauthorized("No license associated with this account".to_string())
    })?;

    let result = sqlx::query!(
        "DELETE FROM store_items WHERE id = $1 AND license_id = $2",
        id,
        license_id
    )
    .execute(&state.db)
    .await?;

    if result.rows_affected() == 0 {
        return Err(ApiError::NotFound("Item not found".to_string()));
    }

    Ok(StatusCode::NO_CONTENT)
}

// ============================================================================
// TRANSACTIONS
// ============================================================================

#[derive(Serialize)]
struct StoreTransaction {
    id: Uuid,
    item_id: Option<Uuid>,
    steam_id: String,
    player_name: Option<String>,
    paypal_order_id: String,
    amount: rust_decimal::Decimal,
    currency: String,
    status: String,
    delivered: bool,
    delivered_at: Option<chrono::DateTime<chrono::Utc>>,
    payer_email: Option<String>,
    created_at: chrono::DateTime<chrono::Utc>,
}

async fn list_transactions(
    State(state): State<Arc<AppState>>,
    claims: Claims,
) -> Result<impl IntoResponse, ApiError> {
    let license_id = claims.license_id.ok_or_else(|| {
        ApiError::Unauthorized("No license associated with this account".to_string())
    })?;

    let transactions = sqlx::query_as!(
        StoreTransaction,
        "SELECT id, item_id, steam_id, player_name, paypal_order_id, amount, currency, status, delivered, delivered_at, payer_email, created_at
         FROM store_transactions
         WHERE license_id = $1
         ORDER BY created_at DESC
         LIMIT 100",
        license_id
    )
    .fetch_all(&state.db)
    .await?;

    Ok(Json(transactions))
}
