use anyhow::{Context, Result, bail};
use reqwest::Client;
use serde::{Deserialize, Serialize};
use sqlx::PgPool;
use uuid::Uuid;

/// PayPal subscription processor for webstore feature ($10/mo recurring)
pub struct SubscriptionProcessor {
    client: Client,
    client_id: String,
    client_secret: String,
    sandbox_mode: bool,
    db: PgPool,
}

impl SubscriptionProcessor {
    pub fn new(
        client_id: String,
        client_secret: String,
        sandbox_mode: bool,
        db: PgPool,
    ) -> Self {
        Self {
            client: Client::new(),
            client_id,
            client_secret,
            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
    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?;
        Ok(token_data.access_token)
    }

    /// Create PayPal subscription for webstore feature
    pub async fn create_webstore_subscription(
        &self,
        license_id: Uuid,
        plan_id: &str,  // 'P-xxx' PayPal plan ID for $10/mo
    ) -> Result<String> {
        #[derive(Serialize)]
        struct CreateSubscriptionRequest {
            plan_id: String,
            application_context: ApplicationContext,
        }

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

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

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

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

        let request = CreateSubscriptionRequest {
            plan_id: plan_id.to_string(),
            application_context: ApplicationContext {
                brand_name: "Corrosion Webstore".to_string(),
                return_url: format!("https://panel.corrosionmgmt.com/store/subscription/success"),
                cancel_url: format!("https://panel.corrosionmgmt.com/store/subscription/cancel"),
            },
        };

        let response = self
            .client
            .post(format!("{}/v1/billing/subscriptions", self.api_base()))
            .bearer_auth(&access_token)
            .json(&request)
            .send()
            .await
            .context("Failed to create PayPal subscription")?;

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

        let subscription: CreateSubscriptionResponse = response.json().await?;

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

        Ok(approval_url)
    }

    /// Get subscription details from PayPal
    pub async fn get_subscription_details(&self, subscription_id: &str) -> Result<SubscriptionDetails> {
        let access_token = self.get_access_token().await?;

        let response = self
            .client
            .get(format!(
                "{}/v1/billing/subscriptions/{}",
                self.api_base(),
                subscription_id
            ))
            .bearer_auth(&access_token)
            .send()
            .await
            .context("Failed to get subscription details")?;

        if !response.status().is_success() {
            let body = response.text().await.unwrap_or_default();
            bail!("Failed to retrieve subscription: {}", body);
        }

        let details: SubscriptionDetails = response.json().await?;
        Ok(details)
    }

    /// Cancel subscription
    pub async fn cancel_subscription(&self, subscription_id: &str, reason: &str) -> Result<()> {
        #[derive(Serialize)]
        struct CancelRequest {
            reason: String,
        }

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

        let request = CancelRequest {
            reason: reason.to_string(),
        };

        let response = self
            .client
            .post(format!(
                "{}/v1/billing/subscriptions/{}/cancel",
                self.api_base(),
                subscription_id
            ))
            .bearer_auth(&access_token)
            .json(&request)
            .send()
            .await
            .context("Failed to cancel subscription")?;

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

        Ok(())
    }

    /// Process subscription webhook event
    pub async fn process_subscription_webhook(&self, event: SubscriptionWebhookEvent) -> Result<()> {
        match event.event_type.as_str() {
            "BILLING.SUBSCRIPTION.ACTIVATED" => {
                self.handle_subscription_activated(event).await?;
            }
            "BILLING.SUBSCRIPTION.CANCELLED" => {
                self.handle_subscription_cancelled(event).await?;
            }
            "BILLING.SUBSCRIPTION.SUSPENDED" => {
                self.handle_subscription_suspended(event).await?;
            }
            "BILLING.SUBSCRIPTION.PAYMENT.FAILED" => {
                self.handle_payment_failed(event).await?;
            }
            _ => {
                tracing::info!("Unhandled subscription webhook: {}", event.event_type);
            }
        }
        Ok(())
    }

    async fn handle_subscription_activated(&self, event: SubscriptionWebhookEvent) -> Result<()> {
        tracing::info!("Subscription activated: {:?}", event.resource);
        // Store in webstore_subscriptions table, enable webstore for license
        Ok(())
    }

    async fn handle_subscription_cancelled(&self, event: SubscriptionWebhookEvent) -> Result<()> {
        tracing::warn!("Subscription cancelled: {:?}", event.resource);
        // Mark subscription cancelled, disable webstore at period end
        Ok(())
    }

    async fn handle_subscription_suspended(&self, event: SubscriptionWebhookEvent) -> Result<()> {
        tracing::warn!("Subscription suspended (payment failure): {:?}", event.resource);
        // Mark subscription suspended, send alert to license owner
        Ok(())
    }

    async fn handle_payment_failed(&self, event: SubscriptionWebhookEvent) -> Result<()> {
        tracing::error!("Subscription payment failed: {:?}", event.resource);
        // Notify license owner, mark subscription past_due
        Ok(())
    }
}

#[derive(Debug, Deserialize, Serialize)]
pub struct SubscriptionDetails {
    pub id: String,
    pub status: String,
    pub plan_id: String,
    pub billing_info: Option<BillingInfo>,
}

#[derive(Debug, Deserialize, Serialize)]
pub struct BillingInfo {
    pub next_billing_time: Option<String>,
    pub last_payment: Option<LastPayment>,
}

#[derive(Debug, Deserialize, Serialize)]
pub struct LastPayment {
    pub amount: Amount,
    pub time: String,
}

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

#[derive(Debug, Deserialize, Serialize)]
pub struct SubscriptionWebhookEvent {
    pub id: String,
    pub event_type: String,
    pub resource: serde_json::Value,
}
