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

use crate::{
    models::error::ApiError,
    services::payment_processor::PayPalProcessor,
    AppState,
};

pub fn router() -> Router<Arc<AppState>> {
    Router::new()
        .route("/:subdomain", get(get_store_info))
        .route("/:subdomain/items", get(get_store_items))
        .route("/:subdomain/purchase", post(create_purchase_order))
        .route("/:subdomain/webhook", post(handle_purchase_webhook))
}

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

/// Get public store information by subdomain
async fn get_store_info(
    State(state): State<Arc<AppState>>,
    Path(subdomain): Path<String>,
) -> Result<impl IntoResponse, ApiError> {
    // Get license_id from subdomain
    let license = sqlx::query!("SELECT id FROM licenses WHERE subdomain = $1", subdomain)
        .fetch_optional(&state.db)
        .await?;

    let license_id = license
        .ok_or_else(|| ApiError::NotFound("Store not found".to_string()))?
        .id;

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

    if let Some(config) = config {
        if !config.enabled {
            return Err(ApiError::NotFound("Store is currently disabled".to_string()));
        }
        Ok(Json(config))
    } else {
        Err(ApiError::NotFound("Store not configured".to_string()))
    }
}

#[derive(Serialize)]
struct PublicStoreItem {
    id: Uuid,
    category_name: Option<String>,
    name: String,
    description: Option<String>,
    price: rust_decimal::Decimal,
    image_url: Option<String>,
    item_type: String,
    limit_per_player: Option<i32>,
}

/// Get all items available in the public store
async fn get_store_items(
    State(state): State<Arc<AppState>>,
    Path(subdomain): Path<String>,
) -> Result<impl IntoResponse, ApiError> {
    let license = sqlx::query!("SELECT id FROM licenses WHERE subdomain = $1", subdomain)
        .fetch_optional(&state.db)
        .await?;

    let license_id = license
        .ok_or_else(|| ApiError::NotFound("Store not found".to_string()))?
        .id;

    // Check if store is enabled
    let enabled = sqlx::query_scalar!("SELECT enabled FROM store_config WHERE license_id = $1", license_id)
        .fetch_optional(&state.db)
        .await?
        .unwrap_or(false);

    if !enabled {
        return Err(ApiError::NotFound("Store is currently disabled".to_string()));
    }

    type Row = (
        Uuid,
        Option<String>,
        String,
        Option<String>,
        rust_decimal::Decimal,
        Option<String>,
        String,
        Option<i32>,
    );

    let rows: Vec<Row> = sqlx::query_as(
        "SELECT
            i.id,
            c.name as category_name,
            i.name,
            i.description,
            i.price,
            i.image_url,
            i.item_type,
            i.limit_per_player
         FROM store_items i
         LEFT JOIN store_categories c ON c.id = i.category_id
         WHERE i.license_id = $1 AND i.enabled = true
         ORDER BY c.display_order, i.name",
    )
    .bind(license_id)
    .fetch_all(&state.db)
    .await?;

    let items: Vec<PublicStoreItem> = rows
        .into_iter()
        .map(|row| PublicStoreItem {
            id: row.0,
            category_name: row.1,
            name: row.2,
            description: row.3,
            price: row.4,
            image_url: row.5,
            item_type: row.6,
            limit_per_player: row.7,
        })
        .collect();

    Ok(Json(items))
}

#[derive(Deserialize)]
struct CreatePurchaseRequest {
    item_id: Uuid,
    steam_id: String,
    player_name: String,
}

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

/// Create a PayPal order for a store item purchase
async fn create_purchase_order(
    State(state): State<Arc<AppState>>,
    Path(subdomain): Path<String>,
    Json(req): Json<CreatePurchaseRequest>,
) -> Result<impl IntoResponse, ApiError> {
    let license = sqlx::query!("SELECT id FROM licenses WHERE subdomain = $1", subdomain)
        .fetch_optional(&state.db)
        .await?;

    let license_id = license
        .ok_or_else(|| ApiError::NotFound("Store not found".to_string()))?
        .id;

    // Get store config and check if enabled
    let store_config = sqlx::query!(
        "SELECT paypal_client_id, paypal_client_secret, sandbox_mode, enabled
         FROM store_config
         WHERE license_id = $1",
        license_id
    )
    .fetch_optional(&state.db)
    .await?
    .ok_or_else(|| ApiError::NotFound("Store not configured".to_string()))?;

    if !store_config.enabled {
        return Err(ApiError::BadRequest("Store is currently disabled".to_string()));
    }

    // Get item details
    let item = sqlx::query!(
        "SELECT name, price FROM store_items WHERE id = $1 AND license_id = $2 AND enabled = true",
        req.item_id,
        license_id
    )
    .fetch_optional(&state.db)
    .await?
    .ok_or_else(|| ApiError::NotFound("Item not found or disabled".to_string()))?;

    // Check purchase limit if set
    if let Some(limit) = sqlx::query_scalar!(
        "SELECT limit_per_player FROM store_items WHERE id = $1",
        req.item_id
    )
    .fetch_one(&state.db)
    .await?
    {
        let purchase_count: i64 = sqlx::query_scalar!(
            "SELECT COUNT(*) FROM store_transactions
             WHERE item_id = $1 AND steam_id = $2 AND status IN ('paid', 'delivered')",
            req.item_id,
            req.steam_id
        )
        .fetch_one(&state.db)
        .await?
        .unwrap_or(0);

        if purchase_count >= limit as i64 {
            return Err(ApiError::BadRequest("Purchase limit reached for this item".to_string()));
        }
    }

    // Create PayPal processor using store owner's credentials
    let client_id = store_config.paypal_client_id.ok_or_else(|| {
        ApiError::Internal("Store PayPal credentials not configured".to_string())
    })?;

    let client_secret = store_config.paypal_client_secret.ok_or_else(|| {
        ApiError::Internal("Store PayPal credentials not configured".to_string())
    })?;

    // TODO: Decrypt client_secret using encryption service
    let decrypted_secret = client_secret; // Placeholder - should decrypt

    let processor = PayPalProcessor::new(
        client_id.clone(),
        decrypted_secret,
        std::env::var("PAYPAL_WEBHOOK_ID").unwrap_or_default(),
        store_config.sandbox_mode,
        state.db.clone(),
    );

    // Create PayPal order
    let price_f64: f64 = item.price.to_string().parse().unwrap_or(0.0);
    let approval_url = processor
        .create_order(&item.name, price_f64, Some(req.item_id))
        .await?;

    // Extract order_id from approval_url (it's in the query params)
    let order_id = approval_url
        .split("token=")
        .nth(1)
        .and_then(|s| s.split('&').next())
        .unwrap_or_default()
        .to_string();

    // Store pending transaction
    sqlx::query!(
        "INSERT INTO store_transactions (license_id, item_id, steam_id, player_name, paypal_order_id, amount, currency, status)
         VALUES ($1, $2, $3, $4, $5, $6, 'USD', 'pending')",
        license_id,
        req.item_id,
        req.steam_id,
        req.player_name,
        order_id,
        item.price
    )
    .execute(&state.db)
    .await?;

    Ok(Json(CreatePurchaseResponse {
        order_id: order_id.clone(),
        approval_url,
    }))
}

/// Handle PayPal webhooks for store purchases
async fn handle_purchase_webhook(
    State(state): State<Arc<AppState>>,
    Path(subdomain): Path<String>,
    Json(webhook): Json<serde_json::Value>,
) -> Result<impl IntoResponse, ApiError> {
    tracing::info!("Store purchase webhook for {}: {:?}", subdomain, webhook);

    let license = sqlx::query!("SELECT id FROM licenses WHERE subdomain = $1", subdomain)
        .fetch_optional(&state.db)
        .await?;

    let license_id = license
        .ok_or_else(|| ApiError::NotFound("Store not found".to_string()))?
        .id;

    // TODO: Verify webhook signature

    // Parse event_type
    let event_type = webhook
        .get("event_type")
        .and_then(|v| v.as_str())
        .unwrap_or_default();

    match event_type {
        "PAYMENT.CAPTURE.COMPLETED" => {
            handle_payment_completed(&state, license_id, webhook).await?;
        }
        "PAYMENT.CAPTURE.DENIED" | "PAYMENT.CAPTURE.REFUNDED" => {
            handle_payment_failed(&state, license_id, webhook).await?;
        }
        _ => {
            tracing::info!("Unhandled store webhook event: {}", event_type);
        }
    }

    Ok(StatusCode::OK)
}

async fn handle_payment_completed(
    state: &AppState,
    license_id: Uuid,
    webhook: serde_json::Value,
) -> Result<(), ApiError> {
    let order_id = webhook
        .pointer("/resource/supplementary_data/related_ids/order_id")
        .and_then(|v| v.as_str())
        .ok_or_else(|| ApiError::Internal("Missing order_id in webhook".to_string()))?;

    // Update transaction status
    let transaction = sqlx::query!(
        "UPDATE store_transactions
         SET status = 'paid', paypal_transaction_id = $1
         WHERE paypal_order_id = $2 AND license_id = $3
         RETURNING item_id, steam_id",
        webhook.get("id").and_then(|v| v.as_str()),
        order_id,
        license_id
    )
    .fetch_optional(&state.db)
    .await?;

    if let Some(txn) = transaction {
        // Get item delivery commands
        if let Some(item_id) = txn.item_id {
            let item = sqlx::query!(
                "SELECT delivery_commands FROM store_items WHERE id = $1",
                item_id
            )
            .fetch_one(&state.db)
            .await?;

            // Parse delivery commands and send via NATS
            if let Some(commands_json) = item.delivery_commands {
                let commands: Vec<String> = serde_json::from_value(commands_json)?;

                // Replace {steam_id} placeholder in commands
                let steam_id = txn.steam_id;
                let replaced_commands: Vec<String> = commands
                    .iter()
                    .map(|cmd| cmd.replace("{steam_id}", &steam_id))
                    .collect();

                // Send commands via NATS to game server
                if let Some(ref nats) = state.nats {
                    for command in replaced_commands {
                        let subject = format!("corrosion.{}.cmd.console", license_id);
                        let payload = serde_json::json!({ "command": command });
                        nats.publish(subject, serde_json::to_vec(&payload)?.into())
                            .await
                            .map_err(|e| ApiError::Internal(format!("NATS publish failed: {}", e)))?;
                    }

                    // Mark as delivered
                    sqlx::query!(
                        "UPDATE store_transactions SET delivered = true, delivered_at = NOW(), status = 'delivered' WHERE paypal_order_id = $1",
                        order_id
                    )
                    .execute(&state.db)
                    .await?;

                    tracing::info!("Delivered store purchase: order_id={}", order_id);
                }
            }
        }
    }

    Ok(())
}

async fn handle_payment_failed(
    state: &AppState,
    license_id: Uuid,
    webhook: serde_json::Value,
) -> Result<(), ApiError> {
    let order_id = webhook
        .pointer("/resource/supplementary_data/related_ids/order_id")
        .and_then(|v| v.as_str())
        .ok_or_else(|| ApiError::Internal("Missing order_id in webhook".to_string()))?;

    sqlx::query!(
        "UPDATE store_transactions SET status = 'failed' WHERE paypal_order_id = $1 AND license_id = $2",
        order_id,
        license_id
    )
    .execute(&state.db)
    .await?;

    tracing::warn!("Store payment failed: order_id={}", order_id);
    Ok(())
}
