use axum::extract::State;
use axum::http::StatusCode;
use axum::response::{IntoResponse, Redirect, Response};

pub async fn checkout(
  State(state): State<crate::AppState>,
) -> Result<Response, (StatusCode, String)> {
  let price_id = &state.config.stripe_price_id;
  if price_id.is_empty() {
    return Err((
      StatusCode::INTERNAL_SERVER_ERROR,
      "Stripe price not configured".to_string(),
    ));
  }

  let success_url = format!(
    "{}/success?session_id={{CHECKOUT_SESSION_ID}}",
    state.config.app_url
  );
  let cancel_url = format!("{}/", state.config.app_url);

  let params = stripe::CreateCheckoutSession {
    mode: Some(stripe::CheckoutSessionMode::Payment),
    line_items: Some(vec![stripe::CreateCheckoutSessionLineItems {
      price: Some(price_id.to_string()),
      quantity: Some(1),
      ..Default::default()
    }]),
    success_url: Some(&success_url),
    cancel_url: Some(&cancel_url),
    ..Default::default()
  };

  let session = stripe::CheckoutSession::create(&state.stripe_client, params)
    .await
    .map_err(|e| {
      (
        StatusCode::INTERNAL_SERVER_ERROR,
        format!("Stripe error: {}", e),
      )
    })?;

  let url = session.url.ok_or((
    StatusCode::INTERNAL_SERVER_ERROR,
    "No checkout URL returned".to_string(),
  ))?;

  Ok(Redirect::to(&url).into_response())
}

#[cfg(test)]
mod tests {
  use super::*;
  use ed25519_dalek::SigningKey;
  use sqlx::sqlite::SqlitePoolOptions;

  fn test_config() -> crate::config::Config {
    crate::config::Config {
      stripe_secret_key: "sk_test_fake".to_string(),
      stripe_price_id: "price_test_123".to_string(),
      signing_key_hex: "0".repeat(64),
      database_url: "sqlite::memory:".to_string(),
      release_dir: "/tmp/releases".to_string(),
      static_dir: "./static".to_string(),
      port: 3000,
      app_url: "http://localhost:3000".to_string(),
    }
  }

  #[tokio::test]
  async fn test_checkout_missing_price_id() {
    let key = SigningKey::from_bytes(&[42u8; 32]);
    let pool = SqlitePoolOptions::new()
      .connect("sqlite::memory:")
      .await
      .unwrap();

    let state = crate::AppState {
      config: crate::config::Config {
        stripe_price_id: String::new(),
        ..test_config()
      },
      db: pool,
      signing_key: key,
      stripe_client: stripe::Client::new("sk_test_fake"),
    };

    let result = checkout(State(state)).await;
    assert!(result.is_err());
    let (status, msg) = result.unwrap_err();
    assert_eq!(status, StatusCode::INTERNAL_SERVER_ERROR);
    assert!(msg.contains("not configured"));
  }
}
