"""
Stripe Payment Service

Handles subscription payments for pounce.ch

Subscription Tiers:
- Scout (Free): $0/month
- Trader: $9/month
- Tycoon: $29/month

Environment Variables Required:
- STRIPE_SECRET_KEY: Stripe API secret key
- STRIPE_WEBHOOK_SECRET: Webhook signing secret
- STRIPE_PRICE_TRADER: Price ID for Trader plan
- STRIPE_PRICE_TYCOON: Price ID for Tycoon plan
"""
import logging
import os
from typing import Optional, Dict, Any
from datetime import datetime

import stripe
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession

from app.models.user import User
from app.models.subscription import Subscription, SubscriptionTier, SubscriptionStatus

logger = logging.getLogger(__name__)

# Initialize Stripe with API key from environment
stripe.api_key = os.getenv("STRIPE_SECRET_KEY")

# Price IDs from Stripe Dashboard
STRIPE_PRICES = {
    "trader": os.getenv("STRIPE_PRICE_TRADER"),
    "tycoon": os.getenv("STRIPE_PRICE_TYCOON"),
}

# Subscription tier features
TIER_FEATURES = {
    "scout": {
        "name": "Scout",
        "price": 0,
        "currency": "USD",
        "max_domains": 5,
        "check_frequency": "daily",
        "portfolio_domains": 0,
        "features": ["Basic monitoring", "Daily checks", "Email alerts"],
    },
    "trader": {
        "name": "Trader",
        "price": 9,
        "currency": "USD",
        "max_domains": 50,
        "check_frequency": "hourly",
        "portfolio_domains": 25,
        "features": [
            "50 domain monitoring",
            "Hourly checks",
            "Portfolio tracking (25 domains)",
            "Domain valuation",
            "Market insights",
            "SMS/Telegram alerts",
        ],
    },
    "tycoon": {
        "name": "Tycoon",
        "price": 29,
        "currency": "USD",
        "max_domains": 500,
        "check_frequency": "realtime",
        "portfolio_domains": -1,  # Unlimited
        "features": [
            "500+ domain monitoring",
            "10-minute checks",
            "Unlimited portfolio",
            "API access",
            "Bulk tools",
            "SEO metrics",
            "Priority support",
            "Webhooks",
        ],
    },
}


class StripeService:
    """
    Handles Stripe payment operations.
    
    Usage:
    1. Create checkout session for user
    2. User completes payment on Stripe
    3. Webhook updates subscription status
    """
    
    @staticmethod
    def is_configured() -> bool:
        """Check if Stripe is properly configured."""
        return bool(stripe.api_key)
    
    @staticmethod
    async def create_checkout_session(
        user: User,
        plan: str,
        success_url: str,
        cancel_url: str,
    ) -> Dict[str, Any]:
        """
        Create a Stripe Checkout session for subscription.
        
        Args:
            user: User subscribing
            plan: "trader" or "tycoon"
            success_url: URL to redirect after successful payment
            cancel_url: URL to redirect if user cancels
            
        Returns:
            Dict with checkout_url and session_id
        """
        if not StripeService.is_configured():
            raise ValueError("Stripe is not configured. Set STRIPE_SECRET_KEY environment variable.")
        
        if plan not in ["trader", "tycoon"]:
            raise ValueError(f"Invalid plan: {plan}. Must be 'trader' or 'tycoon'")
        
        price_id = STRIPE_PRICES.get(plan)
        if not price_id:
            raise ValueError(f"Price ID not configured for plan: {plan}")
        
        try:
            # Create or get Stripe customer
            if user.stripe_customer_id:
                customer_id = user.stripe_customer_id
            else:
                customer = stripe.Customer.create(
                    email=user.email,
                    name=user.name or user.email,
                    metadata={
                        "user_id": str(user.id),
                        "pounce_user": "true",
                    }
                )
                customer_id = customer.id
                # Note: Save customer_id to user in calling code
            
            # Create checkout session
            session = stripe.checkout.Session.create(
                customer=customer_id,
                payment_method_types=["card"],
                line_items=[
                    {
                        "price": price_id,
                        "quantity": 1,
                    }
                ],
                mode="subscription",
                success_url=success_url,
                cancel_url=cancel_url,
                metadata={
                    "user_id": str(user.id),
                    "plan": plan,
                },
                subscription_data={
                    "metadata": {
                        "user_id": str(user.id),
                        "plan": plan,
                    }
                },
            )
            
            return {
                "checkout_url": session.url,
                "session_id": session.id,
                "customer_id": customer_id,
            }
            
        except stripe.error.StripeError as e:
            logger.error(f"Stripe error creating checkout: {e}")
            raise
    
    @staticmethod
    async def create_portal_session(
        customer_id: str,
        return_url: str,
    ) -> str:
        """
        Create a Stripe Customer Portal session for managing subscription.
        
        Users can:
        - Update payment method
        - View invoices
        - Cancel subscription
        """
        if not StripeService.is_configured():
            raise ValueError("Stripe is not configured")
        
        try:
            session = stripe.billing_portal.Session.create(
                customer=customer_id,
                return_url=return_url,
            )
            return session.url
        except stripe.error.StripeError as e:
            logger.error(f"Stripe error creating portal session: {e}")
            raise
    
    @staticmethod
    async def cancel_subscription(stripe_subscription_id: str) -> bool:
        """
        Cancel a subscription in Stripe.
        
        Args:
            stripe_subscription_id: The Stripe subscription ID to cancel
            
        Returns:
            True if cancelled successfully, False otherwise
        """
        if not StripeService.is_configured():
            logger.warning("Stripe not configured, skipping cancel")
            return False
        
        if not stripe_subscription_id:
            logger.warning("No Stripe subscription ID provided")
            return False
        
        try:
            # Cancel the subscription immediately
            stripe.Subscription.cancel(stripe_subscription_id)
            logger.info(f"Cancelled Stripe subscription: {stripe_subscription_id}")
            return True
        except stripe.error.InvalidRequestError as e:
            # Subscription might already be cancelled
            logger.warning(f"Stripe subscription cancel failed (may already be cancelled): {e}")
            return True  # Consider it success if already cancelled
        except stripe.error.StripeError as e:
            logger.error(f"Stripe error cancelling subscription: {e}")
            return False

    @staticmethod
    async def handle_webhook(
        payload: bytes,
        sig_header: str,
        db: AsyncSession,
    ) -> Dict[str, Any]:
        """
        Handle Stripe webhook events.
        
        Important events:
        - checkout.session.completed: Payment successful (initial)
        - invoice.payment_succeeded: Invoice paid (recurring & initial)
        - customer.subscription.updated: Subscription changed
        - customer.subscription.deleted: Subscription cancelled
        - invoice.payment_failed: Payment failed
        """
        webhook_secret = os.getenv("STRIPE_WEBHOOK_SECRET")
        
        if not webhook_secret:
            raise ValueError("STRIPE_WEBHOOK_SECRET not configured")
        
        try:
            event = stripe.Webhook.construct_event(
                payload, sig_header, webhook_secret
            )
        except ValueError:
            logger.error("❌ Invalid webhook payload")
            raise ValueError("Invalid payload")
        except stripe.error.SignatureVerificationError:
            logger.error("❌ Invalid webhook signature")
            raise ValueError("Invalid signature")
        
        event_type = event["type"]
        data = event["data"]["object"]
        
        logger.info(f"🔔 Processing Stripe webhook: {event_type}")
        logger.info(f"   Event ID: {event.get('id')}")
        
        try:
            if event_type == "checkout.session.completed":
                await StripeService._handle_checkout_complete(data, db)
            
            elif event_type == "invoice.payment_succeeded":
                # This is the main event for successful payments!
                await StripeService._handle_invoice_paid(data, db)
            
            elif event_type == "customer.subscription.updated":
                await StripeService._handle_subscription_updated(data, db)
            
            elif event_type == "customer.subscription.deleted":
                await StripeService._handle_subscription_cancelled(data, db)
            
            elif event_type == "invoice.payment_failed":
                await StripeService._handle_payment_failed(data, db)
            
            else:
                logger.info(f"   Unhandled event type: {event_type} (acknowledged)")
            
            return {"status": "success", "event_type": event_type}
            
        except Exception as e:
            logger.exception(f"❌ Error processing webhook {event_type}: {e}")
            # Still return success to prevent Stripe from retrying
            # The error is logged for investigation
            return {"status": "error_logged", "event_type": event_type, "error": str(e)}
    
    @staticmethod
    async def _handle_checkout_complete(data: Dict, db: AsyncSession):
        """
        Handle successful checkout - activate subscription.
        
        IMPORTANT: This must be idempotent! Stripe may send webhooks multiple times.
        """
        session_id = data.get("id")
        user_id = data.get("metadata", {}).get("user_id")
        plan = data.get("metadata", {}).get("plan")  # "trader" or "tycoon"
        customer_id = data.get("customer")
        subscription_id = data.get("subscription")
        
        logger.info(f"🔔 Checkout complete webhook received:")
        logger.info(f"   Session: {session_id}")
        logger.info(f"   User ID: {user_id}")
        logger.info(f"   Plan: {plan}")
        logger.info(f"   Customer: {customer_id}")
        logger.info(f"   Subscription: {subscription_id}")
        
        if not user_id or not plan:
            logger.error(f"❌ Missing user_id or plan in checkout metadata: {data.get('metadata')}")
            return
        
        # Convert plan string to SubscriptionTier enum
        tier_map = {
            "trader": SubscriptionTier.TRADER,
            "tycoon": SubscriptionTier.TYCOON,
            "scout": SubscriptionTier.SCOUT,
        }
        tier_enum = tier_map.get(plan.lower(), SubscriptionTier.SCOUT)
        
        # Get user
        result = await db.execute(
            select(User).where(User.id == int(user_id))
        )
        user = result.scalar_one_or_none()
        
        if not user:
            logger.error(f"❌ User {user_id} not found for checkout")
            return
        
        logger.info(f"   User email: {user.email}")
        
        # IDEMPOTENCY CHECK: Check if this subscription_id was already processed
        if subscription_id:
            existing_sub = await db.execute(
                select(Subscription).where(
                    Subscription.stripe_subscription_id == subscription_id
                )
            )
            existing = existing_sub.scalar_one_or_none()
            if existing:
                logger.info(f"⚠️ Subscription {subscription_id} already processed (idempotent)")
                return
        
        # Update user's Stripe customer ID
        user.stripe_customer_id = customer_id
        
        # Create or update subscription
        sub_result = await db.execute(
            select(Subscription).where(Subscription.user_id == user.id)
        )
        subscription = sub_result.scalar_one_or_none()
        
        tier_info = TIER_FEATURES.get(plan.lower(), TIER_FEATURES["scout"])
        
        if subscription:
            # Only upgrade if actually changing
            old_tier = subscription.tier
            subscription.tier = tier_enum
            subscription.status = SubscriptionStatus.ACTIVE
            subscription.stripe_subscription_id = subscription_id
            subscription.max_domains = tier_info["max_domains"]
            subscription.check_frequency = tier_info["check_frequency"]
            subscription.updated_at = datetime.utcnow()
            logger.info(f"✅ Updated subscription: {old_tier} → {tier_enum}")
        else:
            subscription = Subscription(
                user_id=user.id,
                tier=tier_enum,
                status=SubscriptionStatus.ACTIVE,
                stripe_subscription_id=subscription_id,
                max_domains=tier_info["max_domains"],
                check_frequency=tier_info["check_frequency"],
            )
            db.add(subscription)
            logger.info(f"✅ Created new subscription: {tier_enum}")
        
        try:
            await db.commit()
            logger.info(f"✅ Activated {plan} subscription for user {user_id} ({user.email})")
        except Exception as e:
            logger.exception(f"❌ Failed to commit subscription: {e}")
            await db.rollback()
            raise
    
    @staticmethod
    async def _handle_invoice_paid(data: Dict, db: AsyncSession):
        """
        Handle successful invoice payment.
        
        This is the MAIN event for activating subscriptions!
        Called for both initial payments and recurring payments.
        
        Invoice structure has metadata in:
        - parent.subscription_details.metadata (for subscription invoices)
        - lines.data[0].metadata (line item level)
        """
        invoice_id = data.get("id")
        customer_id = data.get("customer")
        customer_email = data.get("customer_email")
        billing_reason = data.get("billing_reason")  # "subscription_create", "subscription_cycle", etc.
        
        logger.info(f"🧾 Invoice paid webhook received:")
        logger.info(f"   Invoice: {invoice_id}")
        logger.info(f"   Customer: {customer_id}")
        logger.info(f"   Email: {customer_email}")
        logger.info(f"   Billing reason: {billing_reason}")
        
        # Extract metadata from subscription details
        parent = data.get("parent", {})
        subscription_details = parent.get("subscription_details", {})
        metadata = subscription_details.get("metadata", {})
        subscription_id = subscription_details.get("subscription")
        
        user_id = metadata.get("user_id")
        plan = metadata.get("plan")
        
        # Fallback: try to get from line items
        if not user_id or not plan:
            lines = data.get("lines", {}).get("data", [])
            if lines:
                line_metadata = lines[0].get("metadata", {})
                user_id = user_id or line_metadata.get("user_id")
                plan = plan or line_metadata.get("plan")
        
        logger.info(f"   User ID: {user_id}")
        logger.info(f"   Plan: {plan}")
        logger.info(f"   Subscription ID: {subscription_id}")
        
        if not user_id or not plan:
            logger.warning(f"⚠️ No user_id or plan in invoice metadata, skipping")
            logger.warning(f"   Full parent: {parent}")
            return
        
        # Convert plan string to SubscriptionTier enum
        tier_map = {
            "trader": SubscriptionTier.TRADER,
            "tycoon": SubscriptionTier.TYCOON,
            "scout": SubscriptionTier.SCOUT,
        }
        tier_enum = tier_map.get(plan.lower(), SubscriptionTier.SCOUT)
        
        # Get user
        result = await db.execute(
            select(User).where(User.id == int(user_id))
        )
        user = result.scalar_one_or_none()
        
        if not user:
            logger.error(f"❌ User {user_id} not found for invoice")
            return
        
        logger.info(f"   Found user: {user.email}")
        
        # Update user's Stripe customer ID if not set
        if not user.stripe_customer_id:
            user.stripe_customer_id = customer_id
        
        # IDEMPOTENCY CHECK: Check if this subscription_id was already processed with this tier
        if subscription_id:
            existing_sub = await db.execute(
                select(Subscription).where(
                    Subscription.stripe_subscription_id == subscription_id,
                    Subscription.tier == tier_enum
                )
            )
            existing = existing_sub.scalar_one_or_none()
            if existing:
                logger.info(f"⚠️ Subscription {subscription_id} already active as {tier_enum} (idempotent)")
                return
        
        # Create or update subscription
        sub_result = await db.execute(
            select(Subscription).where(Subscription.user_id == user.id)
        )
        subscription = sub_result.scalar_one_or_none()
        
        tier_info = TIER_FEATURES.get(plan.lower(), TIER_FEATURES["scout"])
        
        if subscription:
            old_tier = subscription.tier
            subscription.tier = tier_enum
            subscription.status = SubscriptionStatus.ACTIVE
            subscription.stripe_subscription_id = subscription_id
            subscription.max_domains = tier_info["max_domains"]
            subscription.check_frequency = tier_info["check_frequency"]
            subscription.updated_at = datetime.utcnow()
            logger.info(f"✅ Updated subscription: {old_tier} → {tier_enum}")
        else:
            subscription = Subscription(
                user_id=user.id,
                tier=tier_enum,
                status=SubscriptionStatus.ACTIVE,
                stripe_subscription_id=subscription_id,
                max_domains=tier_info["max_domains"],
                check_frequency=tier_info["check_frequency"],
            )
            db.add(subscription)
            logger.info(f"✅ Created new subscription: {tier_enum}")
        
        try:
            await db.commit()
            logger.info(f"✅ Activated {plan} subscription for user {user_id} ({user.email}) via invoice")
        except Exception as e:
            logger.exception(f"❌ Failed to commit subscription: {e}")
            await db.rollback()
            raise

    @staticmethod
    async def _handle_subscription_updated(data: Dict, db: AsyncSession):
        """Handle subscription update (plan change, renewal, etc.)."""
        subscription_id = data.get("id")
        status = data.get("status")
        
        result = await db.execute(
            select(Subscription).where(
                Subscription.stripe_subscription_id == subscription_id
            )
        )
        subscription = result.scalar_one_or_none()
        
        if subscription:
            subscription.is_active = status == "active"
            subscription.updated_at = datetime.utcnow()
            await db.commit()
            logger.info(f"Updated subscription {subscription_id}: status={status}")
    
    @staticmethod
    async def _handle_subscription_cancelled(data: Dict, db: AsyncSession):
        """Handle subscription cancellation - downgrade to Scout."""
        subscription_id = data.get("id")
        
        result = await db.execute(
            select(Subscription).where(
                Subscription.stripe_subscription_id == subscription_id
            )
        )
        subscription = result.scalar_one_or_none()
        
        if subscription:
            subscription.tier = SubscriptionTier.SCOUT  # Use enum
            subscription.status = SubscriptionStatus.ACTIVE  # Scout is still active
            subscription.stripe_subscription_id = None
            subscription.max_domains = TIER_FEATURES["scout"]["max_domains"]
            subscription.check_frequency = TIER_FEATURES["scout"]["check_frequency"]
            subscription.updated_at = datetime.utcnow()
            await db.commit()
            logger.info(f"Cancelled subscription, downgraded to Scout: {subscription_id}")
    
    @staticmethod
    async def _handle_payment_failed(data: Dict, db: AsyncSession):
        """Handle failed payment - send notification, eventually downgrade."""
        subscription_id = data.get("subscription")
        
        # Log the failure - in production, send email notification
        logger.warning(f"Payment failed for subscription: {subscription_id}")
        
        # After multiple failures, Stripe will cancel the subscription
        # which triggers _handle_subscription_cancelled


# Global instance
stripe_service = StripeService()

