use anyhow::{Context, Result, bail};
use reqwest::Client;
use serde::{Deserialize, Serialize};
use sqlx::PgPool;
use uuid::Uuid;
use hmac::{Hmac, Mac};
use sha2::Sha256;

type HmacSha256 = Hmac<Sha256>;

/// PayPal payment processor for module purchases and webstore subscriptions
pub struct PayPalProcessor {
    client: Client,
    client_id: String,
    client_secret: String,
    webhook_id: String,
    sandbox_mode: bool,
    db: PgPool,
}

impl PayPalProcessor {
    pub fn new(
        client_id: String,
        client_secret: String,
        webhook_id: String,
        sandbox_mode: bool,
        db: PgPool,
    ) -> Self {
        Self {
            client: Client::new(),
            client_id,
            client_secret,
            webhook_id,
            sandbox_mode,
            db,
        }
    }

    fn api_base(&self) -> &str {
        if self.sandbox_mode {
            "https://api-m.sandbox.paypal.com"
        } else {
            "https://api-m.paypal.com"
        }
    }

    /// Get OAuth access token from PayPal
    async fn get_access_token(&self) -> Result<String> {
        #[derive(Deserialize)]
        struct TokenResponse {
            access_token: String,
        }

        let response = self
            .client
            .post(format!("{}/v1/oauth2/token", self.api_base()))
            .basic_auth(&self.client_id, Some(&self.client_secret))
            .form(&[("grant_type", "client_credentials")])
            .send()
            .await
            .context("Failed to request PayPal access token")?;

        if !response.status().is_success() {
            let body = response.text().await.unwrap_or_default();
            bail!("PayPal OAuth failed: {}", body);
        }

        let token_data: TokenResponse = response
            .json()
            .await
            .context("Failed to parse PayPal token response")?;

        Ok(token_data.access_token)
    }

    /// Create PayPal order for module purchase
    pub async fn create_module_purchase_order(
        &self,
        module_id: Uuid,
        license_id: Uuid,
        amount: f64,
        module_name: &str,
    ) -> Result<String> {
        #[derive(Serialize)]
        struct CreateOrderRequest {
            intent: String,
            purchase_units: Vec<PurchaseUnit>,
            application_context: ApplicationContext,
        }

        #[derive(Serialize)]
        struct PurchaseUnit {
            reference_id: String,
            description: String,
            amount: Amount,
        }

        #[derive(Serialize)]
        struct Amount {
            currency_code: String,
            value: String,
        }

        #[derive(Serialize)]
        struct ApplicationContext {
            return_url: String,
            cancel_url: String,
        }

        #[derive(Deserialize)]
        struct CreateOrderResponse {
            id: String,
            links: Vec<Link>,
        }

        #[derive(Deserialize)]
        struct Link {
            rel: String,
            href: String,
        }

        let access_token = self.get_access_token().await?;

        let request = CreateOrderRequest {
            intent: "CAPTURE".to_string(),
            purchase_units: vec![PurchaseUnit {
                reference_id: format!("module_{}_{}", module_id, license_id),
                description: format!("Corrosion Module: {}", module_name),
                amount: Amount {
                    currency_code: "USD".to_string(),
                    value: format!("{:.2}", amount),
                },
            }],
            application_context: ApplicationContext {
                return_url: format!("https://panel.corrosionmgmt.com/store/modules/success"),
                cancel_url: format!("https://panel.corrosionmgmt.com/store/modules/cancel"),
            },
        };

        let response = self
            .client
            .post(format!("{}/v2/checkout/orders", self.api_base()))
            .bearer_auth(&access_token)
            .json(&request)
            .send()
            .await
            .context("Failed to create PayPal order")?;

        if !response.status().is_success() {
            let body = response.text().await.unwrap_or_default();
            bail!("PayPal order creation failed: {}", body);
        }

        let order: CreateOrderResponse = response
            .json()
            .await
            .context("Failed to parse PayPal order response")?;

        // Find approval URL
        let approval_url = order
            .links
            .iter()
            .find(|link| link.rel == "approve")
            .map(|link| link.href.clone())
            .context("No approval URL in PayPal response")?;

        // Store pending order in database
        self.store_pending_order(&order.id, module_id, license_id, amount)
            .await?;

        Ok(approval_url)
    }

    /// Capture payment after user approves
    pub async fn capture_order(&self, order_id: &str) -> Result<String> {
        #[derive(Deserialize)]
        struct CaptureResponse {
            id: String,
            status: String,
        }

        let access_token = self.get_access_token().await?;

        let response = self
            .client
            .post(format!(
                "{}/v2/checkout/orders/{}/capture",
                self.api_base(),
                order_id
            ))
            .bearer_auth(&access_token)
            .send()
            .await
            .context("Failed to capture PayPal order")?;

        if !response.status().is_success() {
            let body = response.text().await.unwrap_or_default();
            bail!("PayPal capture failed: {}", body);
        }

        let capture: CaptureResponse = response
            .json()
            .await
            .context("Failed to parse capture response")?;

        if capture.status != "COMPLETED" {
            bail!("PayPal capture not completed: {}", capture.status);
        }

        Ok(capture.id)
    }

    /// Verify PayPal webhook signature (CRITICAL SECURITY)
    pub fn verify_webhook_signature(
        &self,
        transmission_id: &str,
        timestamp: &str,
        webhook_id: &str,
        event_body: &str,
        cert_url: &str,
        transmission_sig: &str,
    ) -> Result<bool> {
        // PayPal webhook verification uses HMAC-SHA256
        // For production, should verify cert_url certificate and validate against PayPal root CA

        // Construct expected signature input
        let message = format!(
            "{}|{}|{}|{}",
            transmission_id, timestamp, webhook_id, event_body
        );

        // For now, basic verification
        // TODO: Implement full certificate validation in production
        if webhook_id != self.webhook_id {
            tracing::warn!("Webhook ID mismatch: expected {}, got {}", self.webhook_id, webhook_id);
            return Ok(false);
        }

        // In production, verify transmission_sig matches HMAC of message
        // Skipping crypto verification for MVP - rely on webhook ID match + HTTPS
        Ok(true)
    }

    /// Process webhook event from PayPal
    pub async fn process_webhook_event(&self, event: WebhookEvent) -> Result<()> {
        match event.event_type.as_str() {
            "PAYMENT.CAPTURE.COMPLETED" => {
                self.handle_payment_completed(event).await?;
            }
            "PAYMENT.CAPTURE.DENIED" => {
                self.handle_payment_denied(event).await?;
            }
            "BILLING.SUBSCRIPTION.CREATED" => {
                self.handle_subscription_created(event).await?;
            }
            "BILLING.SUBSCRIPTION.CANCELLED" => {
                self.handle_subscription_cancelled(event).await?;
            }
            _ => {
                tracing::info!("Unhandled PayPal webhook event: {}", event.event_type);
            }
        }
        Ok(())
    }

    async fn handle_payment_completed(&self, event: WebhookEvent) -> Result<()> {
        // Extract order ID and transaction details
        // Mark module purchase as complete
        // Trigger module activation
        tracing::info!("Payment completed: {:?}", event.resource);

        // Implementation will call db::modules::record_module_purchase()
        // and trigger module installation

        Ok(())
    }

    async fn handle_payment_denied(&self, event: WebhookEvent) -> Result<()> {
        tracing::warn!("Payment denied: {:?}", event.resource);
        // Mark order as failed, notify user
        Ok(())
    }

    async fn handle_subscription_created(&self, event: WebhookEvent) -> Result<()> {
        // Phase 5: Webstore subscription activation
        tracing::info!("Subscription created: {:?}", event.resource);
        Ok(())
    }

    async fn handle_subscription_cancelled(&self, event: WebhookEvent) -> Result<()> {
        // Phase 5: Webstore deactivation
        tracing::warn!("Subscription cancelled: {:?}", event.resource);
        Ok(())
    }

    async fn store_pending_order(
        &self,
        order_id: &str,
        module_id: Uuid,
        license_id: Uuid,
        amount: f64,
    ) -> Result<()> {
        sqlx::query!(
            r#"
            INSERT INTO payment_orders (order_id, module_id, license_id, amount, status)
            VALUES ($1, $2, $3, $4, 'pending')
            "#,
            order_id,
            module_id,
            license_id,
            amount
        )
        .execute(&self.db)
        .await
        .context("Failed to store pending order")?;

        Ok(())
    }
}

/// PayPal webhook event structure
#[derive(Debug, Deserialize, Serialize)]
pub struct WebhookEvent {
    pub id: String,
    pub event_type: String,
    pub resource: serde_json::Value,
    pub create_time: String,
}

/// Payment order status tracking
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PaymentOrder {
    pub id: Uuid,
    pub order_id: String,
    pub module_id: Option<Uuid>,
    pub license_id: Uuid,
    pub amount: f64,
    pub status: String, // pending, completed, failed, refunded
    pub created_at: chrono::DateTime<chrono::Utc>,
}
