"""
Stripe Payment Provider Implementation
This module implements the PaymentProvider interface for Stripe
"""

import stripe
import structlog
from typing import Dict, Any, Optional
from datetime import datetime

from .payment_client import PaymentProvider, PaymentCustomer, PaymentMethod, Subscription, Invoice


logger = structlog.get_logger()


class StripeProvider(PaymentProvider):
    """
    Stripe implementation of the PaymentProvider interface
    """
    
    def __init__(self, api_key: str, webhook_secret: Optional[str] = None):
        """
        Initialize the Stripe provider with API key
        """
        stripe.api_key = api_key
        self.webhook_secret = webhook_secret
    
    async def create_customer(self, customer_data: Dict[str, Any]) -> PaymentCustomer:
        """
        Create a customer in Stripe
        """
        try:
            stripe_customer = stripe.Customer.create(
                email=customer_data.get('email'),
                name=customer_data.get('name'),
                phone=customer_data.get('phone'),
                metadata=customer_data.get('metadata', {})
            )
            
            return PaymentCustomer(
                id=stripe_customer.id,
                email=stripe_customer.email,
                name=stripe_customer.name,
                created_at=datetime.fromtimestamp(stripe_customer.created)
            )
        except stripe.error.StripeError as e:
            logger.error("Failed to create Stripe customer", error=str(e))
            raise e
    
    async def create_subscription(self, customer_id: str, plan_id: str, payment_method_id: str, trial_period_days: Optional[int] = None) -> Subscription:
        """
        Create a subscription in Stripe
        """
        try:
            # Attach payment method to customer
            stripe.PaymentMethod.attach(
                payment_method_id,
                customer=customer_id,
            )
            
            # Set customer's default payment method
            stripe.Customer.modify(
                customer_id,
                invoice_settings={
                    'default_payment_method': payment_method_id
                }
            )
            
            # Create subscription with trial period if specified
            subscription_params = {
                'customer': customer_id,
                'items': [{'price': plan_id}],
                'default_payment_method': payment_method_id,
            }
            
            if trial_period_days:
                subscription_params['trial_period_days'] = trial_period_days
            
            stripe_subscription = stripe.Subscription.create(**subscription_params)
            
            return Subscription(
                id=stripe_subscription.id,
                customer_id=stripe_subscription.customer,
                plan_id=plan_id,  # Using the price ID as plan_id
                status=stripe_subscription.status,
                current_period_start=datetime.fromtimestamp(stripe_subscription.current_period_start),
                current_period_end=datetime.fromtimestamp(stripe_subscription.current_period_end),
                created_at=datetime.fromtimestamp(stripe_subscription.created)
            )
        except stripe.error.StripeError as e:
            logger.error("Failed to create Stripe subscription", error=str(e))
            raise e
    
    async def update_payment_method(self, customer_id: str, payment_method_id: str) -> PaymentMethod:
        """
        Update the payment method for a customer in Stripe
        """
        try:
            # Attach payment method to customer
            stripe.PaymentMethod.attach(
                payment_method_id,
                customer=customer_id,
            )
            
            # Set as default payment method
            stripe.Customer.modify(
                customer_id,
                invoice_settings={
                    'default_payment_method': payment_method_id
                }
            )
            
            stripe_payment_method = stripe.PaymentMethod.retrieve(payment_method_id)
            
            return PaymentMethod(
                id=stripe_payment_method.id,
                type=stripe_payment_method.type,
                brand=getattr(stripe_payment_method, stripe_payment_method.type, {}).get('brand'),
                last4=getattr(stripe_payment_method, stripe_payment_method.type, {}).get('last4'),
                exp_month=getattr(stripe_payment_method, stripe_payment_method.type, {}).get('exp_month'),
                exp_year=getattr(stripe_payment_method, stripe_payment_method.type, {}).get('exp_year'),
            )
        except stripe.error.StripeError as e:
            logger.error("Failed to update Stripe payment method", error=str(e))
            raise e
    
    async def cancel_subscription(self, subscription_id: str) -> Subscription:
        """
        Cancel a subscription in Stripe
        """
        try:
            stripe_subscription = stripe.Subscription.delete(subscription_id)
            
            return Subscription(
                id=stripe_subscription.id,
                customer_id=stripe_subscription.customer,
                plan_id=subscription_id,  # This would need to be retrieved differently in practice
                status=stripe_subscription.status,
                current_period_start=datetime.fromtimestamp(stripe_subscription.current_period_start),
                current_period_end=datetime.fromtimestamp(stripe_subscription.current_period_end),
                created_at=datetime.fromtimestamp(stripe_subscription.created)
            )
        except stripe.error.StripeError as e:
            logger.error("Failed to cancel Stripe subscription", error=str(e))
            raise e
    
    async def get_invoices(self, customer_id: str) -> list[Invoice]:
        """
        Get invoices for a customer from Stripe
        """
        try:
            stripe_invoices = stripe.Invoice.list(customer=customer_id, limit=100)

            invoices = []
            for stripe_invoice in stripe_invoices:
                # Create base invoice object
                invoice = Invoice(
                    id=stripe_invoice.id,
                    customer_id=stripe_invoice.customer,
                    subscription_id=stripe_invoice.subscription,
                    amount=stripe_invoice.amount_paid / 100.0,  # Convert from cents
                    currency=stripe_invoice.currency,
                    status=stripe_invoice.status,
                    created_at=datetime.fromtimestamp(stripe_invoice.created),
                    due_date=datetime.fromtimestamp(stripe_invoice.due_date) if stripe_invoice.due_date else None,
                    description=stripe_invoice.description
                )

                # Add Stripe-specific URLs as custom attributes
                invoice.invoice_pdf = stripe_invoice.invoice_pdf if hasattr(stripe_invoice, 'invoice_pdf') else None
                invoice.hosted_invoice_url = stripe_invoice.hosted_invoice_url if hasattr(stripe_invoice, 'hosted_invoice_url') else None

                invoices.append(invoice)

            return invoices
        except stripe.error.StripeError as e:
            logger.error("Failed to retrieve Stripe invoices", error=str(e))
            raise e
    
    async def get_subscription(self, subscription_id: str) -> Subscription:
        """
        Get subscription details from Stripe
        """
        try:
            stripe_subscription = stripe.Subscription.retrieve(subscription_id)
            
            return Subscription(
                id=stripe_subscription.id,
                customer_id=stripe_subscription.customer,
                plan_id=subscription_id,  # This would need to be retrieved differently in practice
                status=stripe_subscription.status,
                current_period_start=datetime.fromtimestamp(stripe_subscription.current_period_start),
                current_period_end=datetime.fromtimestamp(stripe_subscription.current_period_end),
                created_at=datetime.fromtimestamp(stripe_subscription.created)
            )
        except stripe.error.StripeError as e:
            logger.error("Failed to retrieve Stripe subscription", error=str(e))
            raise e
    
    async def get_customer(self, customer_id: str) -> PaymentCustomer:
        """
        Get customer details from Stripe
        """
        try:
            stripe_customer = stripe.Customer.retrieve(customer_id)
            
            return PaymentCustomer(
                id=stripe_customer.id,
                email=stripe_customer.email,
                name=stripe_customer.name,
                created_at=datetime.fromtimestamp(stripe_customer.created)
            )
        except stripe.error.StripeError as e:
            logger.error("Failed to retrieve Stripe customer", error=str(e))
            raise e
    
    async def create_setup_intent(self) -> Dict[str, Any]:
        """
        Create a setup intent for saving payment methods in Stripe
        """
        try:
            setup_intent = stripe.SetupIntent.create()
            
            return {
                'client_secret': setup_intent.client_secret,
                'id': setup_intent.id
            }
        except stripe.error.StripeError as e:
            logger.error("Failed to create Stripe setup intent", error=str(e))
            raise e
    
    async def create_payment_intent(self, amount: float, currency: str, customer_id: str, payment_method_id: str) -> Dict[str, Any]:
        """
        Create a payment intent for one-time payments in Stripe
        """
        try:
            payment_intent = stripe.PaymentIntent.create(
                amount=int(amount * 100),  # Convert to cents
                currency=currency,
                customer=customer_id,
                payment_method=payment_method_id,
                confirm=True
            )
            
            return {
                'id': payment_intent.id,
                'client_secret': payment_intent.client_secret,
                'status': payment_intent.status
            }
        except stripe.error.StripeError as e:
            logger.error("Failed to create Stripe payment intent", error=str(e))
            raise e
