import frappe
import stripe
import json
from custom_ui.services import DbService
from frappe.utils import get_url

class StripeService:

    @staticmethod
    def get_stripe_settings(company: str):
        """Fetch Stripe settings for a given company."""
        settings_name = frappe.get_all("Stripe Settings", pluck="name", filters={"custom_company": company})
        if not settings_name:
            frappe.throw(f"Stripe Settings not found for company: {company}")
        settings = frappe.get_doc("Stripe Settings", settings_name[0]) if settings_name else None
        return settings
    
    @staticmethod
    def get_api_key(company: str) -> str:
        """Retrieve the Stripe API key for the specified company."""
        settings = StripeService.get_stripe_settings(company)
        return settings.get_password("secret_key")
    
    @staticmethod
    def get_webhook_secret(company: str) -> str:
        """Retrieve the Stripe webhook secret for the specified company."""
        settings = StripeService.get_stripe_settings(company)
        if not settings.custom_webhook_secret:
            frappe.throw(f"Stripe Webhook Secret not configured for company: {company}")
        return settings.custom_webhook_secret
    
    @staticmethod
    def create_checkout_session(
        company: str, 
        amount: float, 
        service: str, 
        order_num: str, 
        currency: str = "usd", 
        for_advance_payment: bool = False, 
        line_items: list | None = None,
        sales_invoice: str = None
    ) -> stripe.checkout.Session:
        """
        Create a Stripe Checkout Session.
        
        Args:
            company: Company name
            amount: Payment amount (should be the outstanding amount for invoices)
            service: Service description
            order_num: Sales Order name if for_advance_payment is True, otherwise Sales Invoice name
            currency: Currency code (default: "usd")
            for_advance_payment: True if this is an advance/down payment, False for full invoice payment
            line_items: Optional custom line items for the checkout session
            sales_invoice: Sales Invoice name (for full payments)
            
        Returns:
            stripe.checkout.Session object
        """
        stripe.api_key = StripeService.get_api_key(company)
        
        # Determine payment description
        if for_advance_payment:
            description = f"Advance payment for {company}{' - ' + service if service else ''}"
        else:
            description = f"Invoice payment for {company}{' - ' + service if service else ''}"
            if sales_invoice:
                description = f"Invoice {sales_invoice} - {company}"
        
        # Use custom line items if provided and not an advance payment, otherwise create default line item
        line_items = line_items if line_items and not for_advance_payment else [{
            "price_data": {
                "currency": currency.lower(),
                "product_data": {
                    "name": description
                },
                "unit_amount": int(amount * 100),  # Stripe expects amount in cents
            },
            "quantity": 1,
        }]
        
        # Prepare metadata
        metadata = {
            "company": company,
            "payment_type": "advance" if for_advance_payment else "full"
        }
        
        # Add appropriate document reference to metadata
        if for_advance_payment:
            metadata["sales_order"] = order_num
        else:
            metadata["sales_invoice"] = sales_invoice or order_num
            if sales_invoice:
                # Check if there's a related sales order
                invoice_doc = frappe.get_doc("Sales Invoice", sales_invoice)
                if hasattr(invoice_doc, 'items') and invoice_doc.items:
                    for item in invoice_doc.items:
                        if item.sales_order:
                            metadata["sales_order"] = item.sales_order
                            break
        
        session = stripe.checkout.Session.create(
            mode="payment",
            payment_method_types=["card"],
            line_items=line_items,
            metadata=metadata,
            success_url=f"{get_url()}/payment_success?session_id={{CHECKOUT_SESSION_ID}}",
            cancel_url=f"{get_url()}/payment_cancelled",
        )
        
        return session
    
    @staticmethod
    def get_event(payload: bytes, sig_header: str, company: str = None) -> stripe.Event:
        print("DEBUG: Stripe webhook received")
        print(f"DEBUG: Signature header present: {bool(sig_header)}")
        
        # If company not provided, try to extract from payload metadata
        if not company:
            try:
                payload_dict = json.loads(payload)
                print(f"DEBUG: Parsed payload type: {payload_dict.get('type')}")
                
                metadata = payload_dict.get("data", {}).get("object", {}).get("metadata", {})
                print(f"DEBUG: Metadata from payload: {metadata}")
                
                company = metadata.get("company")
                print(f"DEBUG: Extracted company from metadata: {company}")
            except (json.JSONDecodeError, KeyError, AttributeError) as e:
                print(f"DEBUG: Failed to parse payload: {str(e)}")
        
        # If we still don't have a company, reject the webhook
        if not company:
            print("ERROR: Company information missing in webhook payload")
            frappe.throw("Company information missing in webhook payload.")
        
        print(f"DEBUG: Validating webhook signature for company: {company}")
        
        # Validate webhook signature with the specified company's secret
        try:
            event = stripe.Webhook.construct_event(
                payload=payload,
                sig_header=sig_header,
                secret=StripeService.get_webhook_secret(company)
            )
            print(f"DEBUG: Webhook signature validated successfully for company: {company}")
            print(f"DEBUG: Event type: {event.type}")
            print(f"DEBUG: Event ID: {event.id}")
        except ValueError as e:
            print(f"ERROR: Invalid payload: {str(e)}")
            frappe.throw(f"Invalid payload: {str(e)}")
        except stripe.error.SignatureVerificationError as e:
            print(f"ERROR: Invalid signature for company {company}: {str(e)}")
            frappe.throw(f"Invalid signature for company {company}: {str(e)}")
            
        return event
    
    @staticmethod
    def get_session_and_metadata(payload: bytes, sig_header: str, company: str = None) -> tuple[stripe.checkout.Session, dict]:
        """Retrieve the Stripe Checkout Session and its metadata from a webhook payload."""
        event = StripeService.get_event(payload, sig_header, company)
        if event.type != "checkout.session.completed":
            frappe.throw(f"Unhandled event type: {event.type}")
        session = event.data.object
        metadata = session["metadata"] if "metadata" in session else {}
        
        return session, metadata
    
    