import traceback
from datetime import datetime, timedelta

import pydash
import stripe

from configs.firebase import SERVER_TIMESTAMP, db
from functions.Response import API_Error
from functions.Users import getUser, updateUserTasks

DONT_CHARGE_USERS = [
    "H3VPShrDPIXOqRQdep6EnBp7zXZ2",
    "DboCIpJzyRheyl0lAOGjXkVgS482",
    "EFaAqAIzycYSjZIcIGINY4waB862",
    "BhBFqJmts0TIBB4be9IIOpX4coq2",
    "ghhifp7lQISmR5Ixie4WltcyJO43",
    "ad0WwLLxhAODJzg58kmBqA9ys4j1",
    "C231P1oJ20TjvQKpCxpkDRZJYpc2",
    "XiZwBRHZtmXeJREtBhLVQVdJm6f1",
    "tL328gQi1HMyTQ3Y0QuhvALmKGE2",
    "beSY66Q0qhPrRHVEI8voofYTVXD2",
    "XiZwBRHZtmXeJREtBhLVQVdJm6f1",
    "UTFn1YJfY1cRufnyM5FNhd45ekn1",
    "3d2ulpLKdFN6SWJepyc4KHAEStF2",
    "Gu6B9cWm13f4Zasy7bFxsorcrmA3",
    "SZKPqZdhkqcBbI0z3lo0ZJsELlg2",
    "xp1SNO97xNblzNm7FuDTsN2HYb83",
    "tL328gQi1HMyTQ3Y0QuhvALmKGE2",
    "ZW62xDtX4ddJVJMaaUcrcmBZGkO2",  # 85merch@85supply.com
    "iVMheWpTx0hNr43wt9cYY09ussy2",  # thomasmac@85supply.com
    "2omMbm2OSHXOxGfgfJoRNjRsyDh1",  # defigear@85supply.com
    # "p510LF4wLkhM88ybNKaH1LdxoCu2",  # rumreefapparel@gmail.com
    # "lL5gKEFEWOO3FATxSg5YHhLEKdC3",  # mark@coedsportswear.com
    "cVlvEpN6XYe0YTBQJNYzDKrvBFg2",  # seller1@candlebuilders.com
    "Vv40VRMA7ZfKUcUbgGkwy9K83M92",  # tpvia01@gmail.com
    "PhDBWLh23xM4DKiMXmOs2mSf5L32",  # collision@85supply.com
]


stripe.api_version = "2017-08-15"


def sendInvoice(params: dict):
    user = params.get("currentUser")
    uid, enterpriseId, displayName, isEnterpriseAdmin = (
        user.get("uid"),
        user.get("enterpriseId"),
        user.get("displayName"),
        user.get("isEnterpriseAdmin"),
    )
    # if uid == "R7UPQvH1cOUPPHcgOVtcaqRycAA3": return
    id = params.get("id")
    invoiceRef = db.collection("invoices").document(id).get()
    isProUser = user.get("isProUser")
    if isProUser:
        enterpriseId = "cfbZh6XFBG3usCcUwpRE"
    if invoiceRef.exists and isEnterpriseAdmin:
        invoice = invoiceRef.to_dict()
        userId = invoice.get("userId")
        stripeCreds = (
            db.collection("paymentPlatformsCredentials")
            .document("STRIPE" + enterpriseId)
            .get()
        )
        if stripeCreds.exists:
            stripeCreds = stripeCreds.to_dict()
            apiKey = stripeCreds.get("apiSecret")
            stripe.api_key = apiKey
            customer = getCustomer(userId, enterpriseId)
            if not customer:
                customer = createCustomer(getUser(userId), stripeCreds)
            if customer:
                customerId = customer.get("customerId")
                description = ""
                metadata = dict(
                    uid=uid,
                    invoiceId=id,
                    enterpriseId=enterpriseId,
                    displayName=displayName,
                )
                try:
                    stripe.InvoiceItem.create(
                        customer=customerId,
                        amount=int(invoice.get("totalCost") * 100),
                        currency="USD",
                        description=description,
                        metadata=metadata,
                    )
                    stripeInvoice = stripe.Invoice.create(
                        customer=customerId,
                        collection_method="send_invoice",
                        description=description,
                        metadata=metadata,
                        days_until_due=30,
                    )
                    stripeInvoice = dict(
                        stripe.Invoice.finalize_invoice(stripeInvoice.get("id"))
                    )
                except Exception as e:
                    print(e)
                    raise API_Error(str(e), 400)
                invoiceRef.reference.update(
                    dict(
                        platformInvoiceId=stripeInvoice.get("id"),
                        paymentUrl=stripeInvoice.get("hosted_invoice_url"),
                        platformId="STRIPE",
                        platformName="Stripe",
                        platformAccountName=stripeInvoice.get("account_name"),
                        platformCustomerId=customerId,
                        sent=True,
                        sentAt=SERVER_TIMESTAMP,
                    )
                )
                return dict(invoice=invoiceRef.reference.get().to_dict())
            else:
                raise API_Error("Customer not found.", 404)
        else:
            raise API_Error("Stripe API Keys not saved.", 404)
    raise API_Error("Invoice not found.", 404, dict(id=id))


def getCustomer(userId, enterpriseId):
    ref = (
        db.collection("paymentPlatformsCredentials")
        .document("STRIPE" + enterpriseId)
        .collection("customers")
        .document(userId)
        .get()
    )
    if ref.exists:
        return ref.to_dict()
    return {}


def createCustomer(user: dict, stripeCreds: dict, enterpriseId=None):
    uid, email, name, enterpriseId = (
        user.get("uid"),
        user.get("email"),
        user.get("displayName"),
        user.get("enterpriseId") if not enterpriseId else enterpriseId,
    )
    isProUser = user.get("isProUser")
    isEnterpriseAdmin = user.get("isEnterpriseAdmin")
    if isProUser or isEnterpriseAdmin:
        "cfbZh6XFBG3usCcUwpRE"
    stripe.api_key = stripeCreds.get("apiSecret")
    customer = dict(
        stripe.Customer.create(
            description="",
            name=name,
            email=email,
            metadata=dict(uid=uid, displayName=name),
        )
    )
    customer = dict(
        id=uid,
        customerId=customer.get("id"),
        name=customer.get("name"),
        email=customer.get("email"),
        metadata=customer.get("metadata"),
        createdAt=SERVER_TIMESTAMP,
        updatedAt=SERVER_TIMESTAMP,
        enterpriseId=user.get("enterpriseId"),
        uid=uid,
    )
    db.collection("paymentPlatformsCredentials").document(
        "STRIPE" + enterpriseId
    ).collection("customers").document(uid).set(customer, merge=True)
    return customer


def getCurrency(enterpriseId):
    ref = (
        db.collection("enterprises")
        .document(enterpriseId)
        .collection("settings")
        .document("CURRENCY")
        .get()
    )
    if ref.exists:
        return ref.to_dict().get("code")
    return "USD"


def markAsPaid(params):
    user = params.get("currentUser")
    enterpriseId, isEnterpriseAdmin = user.get("enterpriseId"), user.get(
        "isEnterpriseAdmin"
    )
    isProUser = user.get("isProUser")
    if isProUser:
        enterpriseId = "cfbZh6XFBG3usCcUwpRE"
    if isEnterpriseAdmin:
        id = params.get("id")
        ref = db.collection("invoices").document(id).get()
        if ref.exists:
            invoice = ref.to_dict()
            invoiceEnterpriseId = invoice.get("enterpriseId")
            sent = invoice.get("sent")
            if enterpriseId == invoiceEnterpriseId:
                invoiceId = invoice.get("platformInvoiceId")
                if invoiceId and sent:
                    creds = (
                        db.collection("paymentPlatformsCredentials")
                        .document("STRIPE" + enterpriseId)
                        .get()
                    )
                    if creds.exists:
                        stripe.api_key = creds.get("apiSecret")
                        try:
                            stripe.Invoice.pay(invoiceId, paid_out_of_band=True)
                        except Exception as e:
                            print(e)
                        invoice["paid"] = True
                        invoice["paidAt"] = datetime.now()
                        invoice["markedAsPaid"] = True
                        ref.reference.update(invoice)
                        return invoice
                else:
                    raise API_Error("Invoice is not sent yet.", 400)
        raise API_Error("Invoice not found.", 404, dict(id=id))
    raise API_Error("Access denied.", 403)


def createOrderInvoice(params):
    user = params.get("currentUser")
    isProUser = user.get("isProUser")
    uid = user.get("uid")
    isEnterpriseAdmin = user.get("isEnterpriseAdmin")
    # enterpriseId = user.get('enterpriseId')
    if isProUser:
        enterpriseId = "cfbZh6XFBG3usCcUwpRE"
    orderId = params.get("orderId")
    orderInvoiceRef = db.collection("orderInvoices").document(orderId).get()
    orderInvoice = orderInvoiceRef.to_dict()
    enterpriseId = orderInvoice.get("enterpriseId")
    customer = getCustomer(uid, enterpriseId)
    if isEnterpriseAdmin:
        currency = "USD"
    else:
        currency = getCurrency(enterpriseId)
    user = getUser(orderInvoice.get("userId"))
    # if orderInvoice.get("userId") == "R7UPQvH1cOUPPHcgOVtcaqRycAA3" and enterpriseId == "3pTMy91nsUIWWIsGF2eW": return
    if orderInvoice.get("userId") in [
        "H3VPShrDPIXOqRQdep6EnBp7zXZ2",
        "C231P1oJ20TjvQKpCxpkDRZJYpc2",
        "XiZwBRHZtmXeJREtBhLVQVdJm6f1",
        "tL328gQi1HMyTQ3Y0QuhvALmKGE2",
    ]:
        return
    stripeCreds = (
        db.collection("paymentPlatformsCredentials")
        .document("STRIPE" + enterpriseId)
        .get()
    )
    if not customer:
        customer = createCustomer(user, stripeCreds)
    if stripeCreds.exists:
        stripeCreds = stripeCreds.to_dict()
        customerId = customer.get("customerId")
        description = f"Invoice for order - {orderInvoice.get('platformOrderId')}."
        metadata = dict(
            uid=user.get("uid"),
            enterpriseId=enterpriseId,
            displayName=user.get("displayName"),
            orderId=orderId,
        )
        invoiceItemId = None
        try:
            stripe.api_key = stripeCreds.get("apiSecret")
            invoiceItem = stripe.InvoiceItem.create(
                customer=customerId,
                amount=int(orderInvoice.get("totalCost") * 100),
                currency=currency,
                description=description,
                metadata=metadata,
            )
            invoiceItemId = invoiceItem.get("id")
            # if enterpriseId != "cfbZh6XFBG3usCcUwpRE":
            #     stripeInvoice = stripe.Invoice.create(customer = customerId, collection_method='charge_automatically', description=description,metadata =metadata , currency =currency)
            #     stripeInvoice = dict(stripe.Invoice.finalize_invoice(stripeInvoice.get('id')))
        except Exception as e:
            print(e)
            raise API_Error(str(e), 400)
        # if enterpriseId != "cfbZh6XFBG3usCcUwpRE":
        #     orderInvoiceRef.reference.update(dict(
        #     platformInvoiceId = stripeInvoice.get('id'),
        #     paymentUrl = stripeInvoice.get('hosted_invoice_url'),
        #     platformId = "STRIPE",
        #     platformName = "Stripe",
        #     platformAccountName = stripeInvoice.get('account_name'),
        #     platformCustomerId = customerId,
        #     sent=True,
        #     sentAt=SERVER_TIMESTAMP,
        #     invoiced=True,
        #     ))
        # else:
        orderInvoiceRef.reference.update(
            dict(
                dailyInvoice=True,
                platformId="STRIPE",
                platformName="Stripe",
                platformCustomerId=customerId,
                sent=True,
                sentAt=SERVER_TIMESTAMP,
                invoiced=False,
            )
        )
        return dict(invoice=orderInvoiceRef.reference.get().to_dict())
    else:
        raise API_Error("Stripe API Keys not saved.", 404)


def savePaymenMethod(params: dict):
    currentUser = params.get("currentUser")
    isEnterpriseAdmin = currentUser.get("isEnterpriseAdmin")
    isProUser = currentUser.get("isProUser")
    uid, enterpriseId = currentUser.get("uid"), currentUser.get("enterpriseId")
    if isProUser or isEnterpriseAdmin:
        enterpriseId = "cfbZh6XFBG3usCcUwpRE"
    stripeCreds = (
        db.collection("paymentPlatformsCredentials")
        .document("STRIPE" + enterpriseId)
        .get()
    )
    paymentMethod = params.get("paymentMethod")
    if stripeCreds.exists:
        stripeCreds = stripeCreds.to_dict()
        stripe.api_key = stripeCreds.get("apiSecret")
        customer = getCustomer(uid, enterpriseId=enterpriseId)
        if not customer:
            customer = createCustomer(currentUser, stripeCreds)
        try:
            if paymentMethod.get("type") == "bank":
                return dict(paymentMethod)
            else:
                stripe.PaymentMethod.attach(
                    paymentMethod.get("id"), customer=customer.get("customerId")
                )
            stripe.Customer.modify(
                customer.get("customerId"),
                invoice_settings=dict(
                    default_payment_method=paymentMethod.get("id"),
                ),
            )
            updateUserTasks(uid, billingCreated=True)
            return paymentMethod
        except Exception as e:
            raise API_Error(str(e), 400)
    raise API_Error("Can't save payment method, contact admin.", 400)


testPaymentMethod = [
    {
        "id": "pm_1OVVbmKc34kmwhxFV0vgJxaB",
        "object": "payment_method",
        "billing_details": {
            "address": {
                "city": None,
                "country": None,
                "line1": None,
                "line2": None,
                "postal_code": "12345",
                "state": None,
            },
            "email": None,
            "name": None,
            "phone": None,
        },
        "card": {
            "brand": "visa",
            "checks": {
                "address_line1_check": None,
                "address_postal_code_check": "pass",
                "cvc_check": "pass",
            },
            "country": "US",
            "display_brand": "visa",
            "exp_month": 2,
            "exp_year": 2025,
            "fingerprint": "NsK5ftM6E6VBsGUP",
            "funding": "credit",
            "generated_from": None,
            "last4": "4242",
            "networks": {"available": ["visa"], "preferred": None},
            "three_d_secure_usage": {"supported": True},
            "wallet": None,
        },
        "created": 1704531118,
        "customer": "cus_PK9nqrG8Bowg72",
        "livemode": False,
        "metadata": {},
        "type": "card",
    }
]


BYPASS_PAYMENT_METHOD = ["UJWIVJdFrEPSPq8w5xh5"]


def deletePaymentMethod(params: dict):
    currentUser = params.get("currentUser")
    isEnterpriseAdmin = currentUser.get("isEnterpriseAdmin")
    isProUser = currentUser.get("isProUser")
    uid, enterpriseId = currentUser.get("uid"), currentUser.get("enterpriseId")
    if isProUser or isEnterpriseAdmin: enterpriseId = "cfbZh6XFBG3usCcUwpRE"
    stripeCreds = (
        db.collection("paymentPlatformsCredentials")
        .document("STRIPE" + enterpriseId)
        .get()
    )
    paymentMethodId = params.get("paymentMethodId")
    if stripeCreds.exists:
        stripeCreds = stripeCreds.to_dict()
        stripe.api_key = stripeCreds.get("apiSecret")
        customer = getCustomer(uid, enterpriseId=enterpriseId)
        if not customer: return
        try:
            stripe.PaymentMethod.detach(paymentMethodId)
            return dict(message="Payment method deleted.")
        except Exception as e: raise API_Error(str(e), 400)
    raise API_Error("Can't delete payment method, contact admin.", 400)

def getPaymentMethods(params: dict):
    currentUser = params.get("currentUser")
    roles = currentUser.get("roles", [])
    isProUser, isEnterpriseAdmin = currentUser.get("isProUser"), currentUser.get(
        "isEnterpriseAdmin"
    )
    uid, enterpriseId = currentUser.get("uid"), currentUser.get("enterpriseId")
    if isProUser or isEnterpriseAdmin:
        enterpriseId = "cfbZh6XFBG3usCcUwpRE"
    stripeCreds = (
        db.collection("paymentPlatformsCredentials")
        .document("STRIPE" + enterpriseId)
        .get()
    )
    if (
        uid == "R7UPQvH1cOUPPHcgOVtcaqRycAA3"
        or "NO_PAYMENT_METHOD" in roles
        or enterpriseId in BYPASS_PAYMENT_METHOD
    ):
        return dict(paymentMethods=testPaymentMethod, stripeCreds=stripeCreds.to_dict())
    if stripeCreds.exists:
        stripeCreds = stripeCreds.to_dict()
        stripe.api_key = stripeCreds.get("apiSecret")
        customer = getCustomer(uid, enterpriseId=enterpriseId)
        if not customer:
            customer = createCustomer(currentUser, stripeCreds, enterpriseId)
        try:
            setupIntent = createSetupIntent(uid, enterpriseId, stripeCreds)
            defaultPaymentMethod = None
            try:
                defaultPaymentMethod = (
                    stripe.Customer.retrieve(customer.get("customerId"))
                    .get("invoice_settings")
                    .get("default_payment_method")
                )
            except Exception as e:
                print(e)
            paymentMethods = stripe.PaymentMethod.list(
                customer=customer.get("customerId"), type="card"
            ).get("data", [])
            paymentMethods.extend(
                stripe.PaymentMethod.list(
                    customer=customer.get("customerId"), type="us_bank_account"
                ).get("data", [])
            )
            if defaultPaymentMethod:
                for paymentMethod in paymentMethods:
                    paymentMethod["default"] = (
                        paymentMethod.get("id") == defaultPaymentMethod
                    )
            if (
                currentUser.get("test")
                or currentUser.get("email") == "apprevtest1@shopify.com"
            ):
                return dict(
                    paymentMethods=testPaymentMethod,
                    stripeCreds=stripeCreds.get("apiKey"),
                    setupIntent=setupIntent,
                )
            return dict(
                paymentMethods=paymentMethods,
                stripeCreds=stripeCreds.get("apiKey"),
                setupIntent=setupIntent,
            )
        except Exception as e:
            raise API_Error(str(e), 400)
    raise API_Error("Can't get payment methods, contact admin.", 400)


def getStripeCreds(enterpriseId):
    ref = (
        db.collection("paymentPlatformsCredentials")
        .document("STRIPE" + enterpriseId)
        .get()
    )
    return ref.to_dict()


def createSubscription(params):
    isEnterpriseAdmin = params.get("currentUser").get("isEnterpriseAdmin")
    uid, enterpriseId, id = (
        params.get("currentUser").get("uid"),
        params.get("currentUser").get("enterpriseId"),
        params.get("id"),
    )
    stripCreds = getStripeCreds("cfbZh6XFBG3usCcUwpRE")
    if uid == "zNAlxdaZG6hKf6vexv6ljqHyP8i1":
        stripCreds["apiSecret"] = "sk_test_zhJQxkYHdbtF9EqZAw8l0HlP"
    if stripCreds:
        stripe.api_key = stripCreds.get("apiSecret")
        customer = getCustomer(uid, "cfbZh6XFBG3usCcUwpRE")
        if not customer:
            customer = createCustomer(params.get("currentUser"), stripCreds)
        try:
            subscription = dict(
                stripe.Subscription.create(
                    customer=customer.get("customerId"),
                    trial_period_days=14,
                    items=[
                        {
                            "price": id,
                        },
                    ],
                    metadata=dict(
                        uid=uid,
                        enterpriseId=enterpriseId,
                        displayName=params.get("currentUser").get("displayName"),
                        email=params.get("currentUser").get("email"),
                    ),
                )
            )
            saveSubscription(uid, enterpriseId, id, subscription)
            return subscription
        except Exception as e:
            print(traceback.print_exc())
            raise API_Error(str(e), 400)


def saveSubscription(uid, enterpriseId, id, subscription):
    subId = subscription.get("id")
    subscription["createdAt"] = SERVER_TIMESTAMP
    subscription["updatedAt"] = SERVER_TIMESTAMP
    subscription["uid"] = uid
    subscription["enterpriseId"] = enterpriseId
    db.collection("subscriptions").document(subId).set(subscription)
    db.collection("enterprises").document(enterpriseId).update(
        dict(
            subscriptionId=subId,
            planId=id,
        )
    )
    db.collection("users").document(uid).update(
        dict(
            subscriptionId=subId,
            planId=id,
        )
    )
    return id


def createSetupIntent(uid, enterpriseId, stripeCreds: dict):
    if stripeCreds:
        stripe.api_key = stripeCreds.get("apiSecret")
        customer = getCustomer(uid, enterpriseId)
        try:
            setupIntent = stripe.SetupIntent.create(
                customer=customer.get("customerId"),
                metadata=dict(
                    uid=uid,
                    enterpriseId=enterpriseId,
                ),
                payment_method_types=["card"],
            )
            return setupIntent
        except Exception as e:
            print(traceback.print_exc())
            raise API_Error(str(e), 400)
    raise API_Error("Can't get payment methods, contact admin.", 400)


def createDailyInvoice(params: dict):
    twoDaysAgo = datetime.now() - timedelta(days=10)
    orderInvoices = (
        db.collection("orderInvoices")
        .where("invoiced", "==", False)
        .where("createdAt", ">", twoDaysAgo)
        .get() 
    )
    users = set(
        (order.get("userId"), order.get("enterpriseId")) for order in orderInvoices
    )
    for user, enterpriseId in users:
        if enterpriseId in [
            "4ARNc3YSVqBDEcHbXa0f",
            "60D7GFDlMFFd6IsK1e58",
            "BWhPnd7E0xk180M3Hpj7",
            "gSo3G298DNypOMvofnaR",
        ]:
            print("enterprise skipped for daily invoice", user, enterpriseId)
            continue
        currency = getCurrency(enterpriseId)
        stripeCreds = getStripeCreds(enterpriseId)
        if user in DONT_CHARGE_USERS:
            print("user skipped for daily invoice", user)
            continue
        if not stripeCreds: 
            print("stripe creds not found for", user)
            continue
        stripe.api_key = stripeCreds.get("apiSecret")
        customer = getCustomer(user, enterpriseId)
        thisUserAllInvoices = [
            doc.to_dict()
            for doc in orderInvoices
            if doc.to_dict().get("userId") == user
        ]
        # return
        groupedInvoices = pydash.chunk(thisUserAllInvoices, 225)
        if customer:
            try:
                clearUninvoicedItems(customer.get("customerId"))
                for thisUserInvoices in groupedInvoices:
                    for orderInvoice in thisUserInvoices:
                        invoiceItem = stripe.InvoiceItem.create(
                            customer=customer.get("customerId"),
                            amount=int(orderInvoice.get("totalCost") * 100),
                            currency=currency,
                            description=f"Invoice for Order: {orderInvoice.get('platformOrderId')}",
                            metadata=dict(
                                orderId=orderInvoice.get("id"),
                                platformOrderId=orderInvoice.get("platformOrderId"),
                            ),
                        )
                        orderInvoice["platformInvoiceItemId"] = invoiceItem.get("id")
                        print("invoiced", orderInvoice.get("id"))
                    stripeInvoice = stripe.Invoice.create(
                        customer=customer.get("customerId"),
                        collection_method="charge_automatically",
                        metadata=dict(
                            uid=user,
                            enterpriseId=enterpriseId,
                        ),
                    )
                    try:
                        platformInvoiceId = stripeInvoice.get("id")
                        stripeInvoice = dict(
                            stripe.Invoice.finalize_invoice(platformInvoiceId)
                        )
                        shippingCost = sum(
                            order.get("shippingCost") for order in thisUserInvoices
                        )
                        printingCost = sum(
                            order.get("printingCost") for order in thisUserInvoices
                        )
                        totalCost = sum(
                            order.get("totalCost") for order in thisUserInvoices
                        )
                        poCost = sum(order.get("poCost") for order in thisUserInvoices)
                        # discount = sum(order.get("discount") for order in thisUserInvoices)
                        orderIds = [order.get("id") for order in thisUserInvoices]
                        platformOrderIds = [
                            order.get("platformOrderId") for order in thisUserInvoices
                        ]
                        platformInvoiceItemIds = [
                            order.get("platformInvoiceItemId")
                            for order in thisUserInvoices
                        ]
                        discount = sum(
                            order.get("discount", 0) for order in thisUserInvoices if order.get("discount")
                        )
                        invoiceId = saveInvoice(
                            uid=user,
                            enterpriseId=enterpriseId,
                            shippingCost=shippingCost,
                            printingCost=printingCost,
                            totalCost=totalCost,
                            poCost=poCost,
                            discount=discount,
                            orderIds=orderIds,
                            platformOrderIds=platformOrderIds,
                            sent=True,
                            paid=True,
                            sentAt=SERVER_TIMESTAMP,
                            paidAt=SERVER_TIMESTAMP,
                            userId=user,
                            platformInvoiceId=platformInvoiceId,
                            paymentUrl=stripeInvoice.get("hosted_invoice_url"),
                            platformInvoiceItemIds=platformInvoiceItemIds,
                            platformCustomerId=customer.get("customerId"),
                        )
                        for orderInvoice in thisUserInvoices:
                            db.document(
                                f"orderInvoices/{orderInvoice.get('id')}"
                            ).update(
                                dict(
                                    platformInvoiceId=platformInvoiceId,
                                    platformInvoiceItemId=orderInvoice.get(
                                        "platformInvoiceItemId"
                                    ),
                                    invoicedAt=SERVER_TIMESTAMP,
                                    invoiced=True,
                                    invoiceId=invoiceId,
                                )
                            )
                        print("Daily invoice created for user: ", user, invoiceId)
                    except Exception as e:
                        print("Error while finalizing invoice: ", user , e)
            except Exception as e:
                print("Error while creating invoice for user: ", user, e)
                traceback.print_exc()
    return


def clearUninvoicedItems(customer_id):
    last_item_id = None

    while True:
        # Fetch a page of invoice items
        invoice_items = stripe.InvoiceItem.list(
            customer=customer_id,
            pending=True,
            limit=100,
            starting_after=last_item_id if last_item_id else None,
        )
        items = invoice_items.get("data", [])
        if not items: break

        for item in items:
            try:
                stripe.InvoiceItem.delete(item.get("id"))
                print("cleared uninvoiced item", item.get("id"))
            except Exception as e:
                print(f"Error deleting invoice item {item.get('id')}: {str(e)}")

        # Check if more items remain
        if invoice_items.get("has_more"):
            last_item_id = items[-1].get("id")
        else:
            break

def saveInvoice(
    uid,
    enterpriseId,
    shippingCost,
    printingCost,
    totalCost,
    poCost,
    sent,
    sentAt,
    paid,
    paidAt,
    userId,
    discount,
    platformInvoiceId=None,
    paymentUrl=None,
    orderIds=[],
    platformOrderIds=[],
    createdAt=SERVER_TIMESTAMP,
    updatedAt=SERVER_TIMESTAMP,
    platformInvoiceItemIds=[],
    platformCustomerId=None,
    paymentMethod=None,
    transactionId=None,
    id=None,
    **kwargs,
):
    invoice = dict(
        id=id,
        uid=uid,
        enterpriseId=enterpriseId,
        shippingCost=shippingCost,
        printingCost=printingCost,
        totalCost=totalCost,
        createdAt=createdAt,
        updatedAt=updatedAt,
        sent=sent,
        sentAt=sentAt,
        paid=paid,
        paidAt=paidAt,
        userId=userId,
        discount=discount,
        poCost=poCost,
        orderIds=orderIds,
        platformOrderIds=platformOrderIds,
        platformInvoiceId=platformInvoiceId,
        paymentUrl=paymentUrl,
        platformInvoiceItemIds=platformInvoiceItemIds,
        platformCustomerId=platformCustomerId,
        paymentMethod=paymentMethod,
        transactionId=transactionId,
        **kwargs,
    )
    if id:
        db.document(f"invoices/{id}").set(invoice)
    else:
        _, ref = db.collection("invoices").add(invoice)
        id = ref.id
        ref.update(dict(id=id))
    return id
    # for orderId in orderIds:
    # db.collection("orderInvoices").document(orderId).update(dict(invoiced=True, invoicedAt = SERVER_TIMESTAMP, invoiceId = ref.id))


def updatePaymentMethod(params: dict):
    uid, enterpriseId, isProUser = (
        params.get("currentUser").get("uid"),
        params.get("currentUser").get("enterpriseId"),
        params.get("currentUser").get("isProUser"),
    )
    paymentMethodId = params.get("paymentMethodId")
    isEnterpriseAdmin = params.get("currentUser").get("isEnterpriseAdmin")
    if isProUser or isEnterpriseAdmin:
        enterpriseId = "cfbZh6XFBG3usCcUwpRE"
    stripeCreds = getStripeCreds(enterpriseId)
    if stripeCreds:
        stripe.api_key = stripeCreds.get("apiSecret")
        customer = getCustomer(uid, enterpriseId)
        try:
            res = stripe.Customer.modify(
                customer.get("customerId"),
                # default_source = paymentMethodId,
                invoice_settings=dict(
                    default_payment_method=paymentMethodId,
                ),
            )
            return res.to_dict()
        except Exception as e:
            raise API_Error(str(e), 500)
    raise API_Error("Stripe Creds not found.")


# def createDailyInvoice2(params:dict):
#     twoDaysAgo = datetime.now() - timedelta(days=9)
#     orderInvoices = [doc.to_dict() for doc in db.collection("orderInvoices").where("invoiced", "==", False).where("createdAt", ">", twoDaysAgo).get()]
#     users = set((order.get("userId"), order.get("enterpriseId")) for order in orderInvoices)
#     print(users)
#     for user,enterpriseId in users:
#         stripeCreds = getStripeCreds(enterpriseId)
#         if not stripeCreds: continue
#         stripe.api_key  = stripeCreds.get("apiSecret")
#         customer = getCustomer(user, enterpriseId)
#         # print(user, enterpriseId)
#         # userInvoices = [order for order in orderInvoices if order.get("userId") == user and order.get("dailyInvoice")]
#         # if userInvoices:
#         #     return print(userInvoices)
#         # else: continue
#         if customer:
#             # generate stripe invoice
#             try:
#                 stripeInvoice = stripe.Invoice.create(
#                     customer=customer.get('customerId'),
#                     collection_method="charge_automatically",
#                     metadata=dict(
#                         uid=user,
#                         enterpriseId=enterpriseId,
#                     )
#                 )
#                 print(user, stripeInvoice.get('id'))
#                 items = []
#                 invoiceItems = stripeInvoice.lines.list(limit=100)
#                 items.extend(invoiceItems.get('data'))
#                 while invoiceItems.get('has_more'):
#                     invoiceItems = stripeInvoice.lines.list(limit=100, starting_after = items[-1].get('id'))
#                     items.extend(invoiceItems.get("data"))
#                 invoiceOrderInvoices = []
#                 for item in items:
#                     metadata = item.get('metadata')
#                     print(metadata)
#                     orderId = metadata.get('orderId')
#                     if orderId:
#                         orderInvoice = db.document(f"orderInvoices/{orderId}").get()
#                         if orderInvoice.exists:
#                             invoiceOrderInvoices.append(orderInvoice.to_dict())
#                         else: print("No Order Invoice", orderId)
#                     else: print("No Order", metadata)
#                 try:
#                     invoiceId = stripeInvoice.get('id')
#                     stripeInvoice = dict(stripe.Invoice.finalize_invoice(invoiceId))
#                     shippingCost = sum(order.get("shippingCost") for order in invoiceOrderInvoices)
#                     printingCost = sum(order.get("printingCost") for order in invoiceOrderInvoices)
#                     totalCost = sum(order.get("totalCost") for order in invoiceOrderInvoices)
#                     poCost = sum(order.get("poCost") for order in invoiceOrderInvoices)
#                     # discount = sum(order.get("discount") for order in invoiceOrderInvoices)
#                     orderIds = [order.get("id") for order in invoiceOrderInvoices]
#                     platformOrderIds = [order.get("platformOrderId") for order in invoiceOrderInvoices]
#                     print(user,invoiceId, orderIds, totalCost, poCost, printingCost, shippingCost)
#                     saveInvoice(
#                         uid=user,
#                         enterpriseId=enterpriseId,
#                         shippingCost=shippingCost,
#                         printingCost=printingCost,
#                         totalCost=totalCost,
#                         poCost=poCost,
#                         discount=0,
#                         orderIds=orderIds,
#                         platformOrderIds=platformOrderIds,
#                         sent=True,
#                         paid=True,
#                         sentAt=SERVER_TIMESTAMP,
#                         paidAt=SERVER_TIMESTAMP,
#                         userId=user,
#                         platformInvoiceId=invoiceId,
#                         paymentUrl=stripeInvoice.get('hosted_invoice_url'),
#                     )
#                     print("Daily invoice created for user: ", user)
#                 except Exception as e:
#                     print(e)
#             except Exception as e:
#                 print(traceback.print_exc())
#     return


def orderChargeInvoices(params):
    Allorders = [
        doc.to_dict()
        for doc in db.collection("orderCharges")
        .where("createdAt", ">=", datetime.now() - timedelta(days=7))
        .where("invoiced", "==", False)
        .get()
    ]
    enterprises = pydash.group_by(Allorders, lambda order: order.get("enterpriseId"))
    for enterpriseId, orders in enterprises.items():
        if enterpriseId in ["cfbZh6XFBG3usCcUwpRE", "60D7GFDlMFFd6IsK1e58"]:
            continue
        total = sum(order.get("total") for order in orders)
        stripeCreds = getStripeCreds("cfbZh6XFBG3usCcUwpRE")
        enterprise = db.document(f"enterprises/{enterpriseId}").get()
        admin = enterprise.get("uid")
        stripe.api_key = stripeCreds.get("apiSecret")
        customer = getCustomer(admin, "cfbZh6XFBG3usCcUwpRE")
        if not customer:
            customer = createCustomer(
                getUser(admin), stripeCreds, "cfbZh6XFBG3usCcUwpRE"
            )
        invoiceTitle = "Order Charges for last week"
        stripe.InvoiceItem.create(
            customer=customer.get("customerId"),
            amount=int(total * 100),
            currency="USD",
            description=f"Invoice : {invoiceTitle}",
            metadata=dict(
                enterpriseId=enterpriseId,
                admin=admin,
            ),
        )
        stripeInvoice = stripe.Invoice.create(
            customer=customer.get("customerId"),
            collection_method="charge_automatically",
            description="Invoice " + invoiceTitle,
            metadata=dict(
                enterpriseId=enterpriseId,
                admin=admin,
            ),
        )
        invoice = dict(
            total=total,
            orderIds=[order.get("orderId") for order in orders],
            enterpriseId=enterpriseId,
            uid=admin,
            id=stripeInvoice.get("id"),
            createdAt=SERVER_TIMESTAMP,
            description=invoiceTitle,
        )
        db.document(f"orderChargesInvoices/{stripeInvoice.get('id')}").set(invoice)
        for order in orders:
            db.document(f"orderCharges/{order.get('orderId')}").update(
                dict(invoiced=True, invoiceId=stripeInvoice.get("id"))
            )
        print("Order Charges Invoice created for enterprise: ", enterpriseId)
    return


def createStripeAccountSession(params: dict):

    currentUser = params.get("currentUser")
    uid, enterpriseId = currentUser.get("uid"), currentUser.get("enterpriseId")
    isProUser, isEnterpriseAdmin = currentUser.get("isProUser"), currentUser.get(
        "isEnterpriseAdmin"
    )

    if isProUser or isEnterpriseAdmin:
        enterpriseId = "cfbZh6XFBG3usCcUwpRE"

    # Get Stripe credentials
    stripeCreds = getStripeCreds(enterpriseId)
    if not stripeCreds:
        raise API_Error("Stripe API Keys not found.", 404)

    # Get the Stripe account ID from params or look it up
    account_id = params.get("account_id")

    # Set the API key and create the account session
    stripe.api_key = stripeCreds.get("apiSecret")
    try:
        account_session = stripe.AccountSession.create(
            account=account_id,
            components={
                "payouts": {
                    "enabled": True,
                    "features": {
                        "instant_payouts": True,
                        "standard_payouts": True,
                        "edit_payout_schedule": True,
                        "external_account_collection": True,
                    },
                },
            },
        )

        return {
            "accountSession": account_session,
            "client_secret": account_session.client_secret,
        }
    except Exception as e:
        print(traceback.print_exc())
        raise API_Error(str(e), 400)


def updateCustomer(stripeCreds: dict, customerId=None, **params):
    uid, email, name = (
        params.get("uid"),
        params.get("email"),
        params.get("displayName"),
    )
    stripe.api_key = stripeCreds.get("apiSecret")
    customer = dict(
        stripe.Customer.modify(
            customerId,
            description="",
            name=name,
            email=email,
            metadata=dict(uid=uid, displayName=name),
        )
    )
    return customer


def convertAmount(amount: float, currency: str) -> float:
    divisor = 100
    amount = float(amount)
    value = int(amount*divisor)
    originalAmount = amount
    data = dict(
        original=originalAmount,
        value=value,
        currency=currency,
        divisor=divisor,
    )
    return data
