import traceback
from datetime import datetime

import pytz

from configs.firebase import db
from functions.Shipstation.Orders import findOrder as findShipstationOrder
from V2.functions.Address.main import Address
from V2.functions.Applications.main import Application
from V2.functions.Etsy.Auth import EtsyClient
from V2.functions.Etsy.Products import getShippingProfile
from V2.functions.Images.main import Image
from V2.functions.LastUpdate import LastUpdate
from V2.functions.Orders.main import Order
from V2.functions.Orders.OrderItem import OrderItem
from V2.functions.Products.Variants import Property, Variant
from V2.functions.Shops.main import Shop, getShopsByPlatformId
from V2.middlewares.auth import API_Error
from V2.Params import Params


def updateAllOrders(params:Params):
    shops = getShopsByPlatformId(platformId="1", credentials= True)
    shops = [s for s in shops if all([s.apiVersion == "v3",s.accessToken, s.tokenType, s.expiresAt])]
    return str([updateShopOrders(shop) for shop in shops])

def updateShopOrders(shop:Shop):
    try:
        shopId = shop.id
        updates:list[str] = []
        application = Application.get(shop.appId)
        lastUpdate = LastUpdate.get(shopId)
        client = EtsyClient(application.apiKey, shop)
        orders = getEtsyShopOrders(client=client, platformShopId= shop.platformShopId, lastUpdated=lastUpdate.timestamp)
        orders.sort(key=lambda x: x.get('updated_timestamp'))
        print("updating orders", shopId, len(orders))
        for order in orders:
            platformOrderId:str = order.get('receipt_id')
            # items = getEtsyOrderItems(client, shop.platformShopId, platformOrderId)
            items = order.get('transactions')
            orderItems = []
            for item in items:
                metadata = {}
                if item.get("shipping_template_id") and item.get("shipping_upgrade"):
                    shippingProfile = getShippingProfile(client, item.get('shipping_template_id'))
                    if shippingProfile:
                        selectedUpgrade = item.get('shipping_upgrade')
                        selectedUpgrade = next((upgrade for upgrade in shippingProfile.get('shipping_profile_upgrades') if upgrade.get('upgrade_name') == selectedUpgrade), None)
                        if selectedUpgrade:
                            metadata['shipping'] = dict(
                                    name = shippingProfile.get('title'),
                                    carrier = shippingProfile.get('origin_country_name'),
                                    service = shippingProfile.get('destination_country_name'),
                                )
                orderItem = OrderItem(
                    uid=shop.uid,
                    enterpriseId=shop.enterpriseId,
                    platformId=shop.platformId,
                    platformProductId= item.get('listing_id'),
                    platformOrderItemId=item.get('transaction_id'),
                    platformOrderId = platformOrderId,
                    quantity = item.get('quantity'),
                    price = item.get('price').get("amount")/item.get('price').get("divisor"),
                    discount = 0,
                    tax=0,
                    subtotal=item.get('price').get("amount")/item.get('price').get("divisor")*item.get('quantity'),
                    total=item.get('price').get("amount")/item.get('price').get("divisor")*item.get('quantity'),
                    variantId= str(item.get('product_id')),
                    name=item.get('title'),
                    shopId=shopId, 
                    createdAt= datetime.fromtimestamp(item.get("created_timestamp")),
                    updatedAt= datetime.fromtimestamp(item.get('created_timestamp')),
                    image=Image.from_dict(getListingImage(client,item.get("listing_id"),item.get("listing_image_id"))),
                    productId=shop.platformId+shop.id+str(item.get("listing_id")),
                    metadata=metadata
                )
                variations = item.get('variations', [])
                orderItem.addVariant(
                    Variant(
                        id=item.get('product_id'),
                        price=orderItem.price,
                        sku=item.get("sku"),
                        properties=[
                        Property(
                            name = variation.get('formatted_name'),
                            id = variation.get('property_id'),
                            value = variation.get('formatted_value')
                        ) for variation in variations
                        ]
                    )
                )
                orderItems.append(orderItem)
            shipstationConnection = shop.shipstationConnection
            if shipstationConnection and not (order.get("first_line")):
                shipstationOrder = findShipstationOrder(Shop.get(shop.shipstationConnection.get("shipstationShopId")), orderNumber = str(platformOrderId), storeId = shipstationConnection.get("platformShopId"))
                if shipstationOrder:
                    shippingAddress = shipstationOrder.get("shipTo")
                    order['first_line'] = shippingAddress.get("street1")
                    order['second_line'] = shippingAddress.get("street2")
                    order['city'] = shippingAddress.get("city")
                    order['state'] = shippingAddress.get("state")
                    order['zip'] = shippingAddress.get("postalCode")
                    order['country_iso'] = shippingAddress.get("country")
            convertedOrder = Order(
                uid=shop.uid,
                platformId=shop.platformId,
                shopId=shopId,
                enterpriseId=shop.enterpriseId,
                cancelled=False,
                draft=False,
                shippingAddress=Address(
                    name=order.get('name'),
                    address1= order.get('first_line'),
                    address2= order.get('second_line'),
                    city = order.get('city'),
                    state = order.get('state'),
                    zip = order.get('zip'),
                    country = order.get('country_iso'),
                    countryCode= order.get('country_iso'),
                    email=order.get('buyer_email'),
                ),
                platformOrderId=platformOrderId, 
                createdAt = datetime.fromtimestamp(order.get('created_timestamp')),
                updatedAt= datetime.fromtimestamp(order.get('updated_timestamp')),
                grandTotal= order.get('grandtotal').get("amount")/order.get('grandtotal').get("divisor"),
                shipped= order.get('is_shipped'),
                currencyCode= order.get("grandtotal").get('currency_code'),
                shippingCost= order.get('total_shipping_cost').get("amount")/order.get('total_shipping_cost').get("divisor"),
                totalDiscount= order.get('discount_amt').get("amount")/order.get('discount_amt').get("divisor"),
                totalTax= order.get('total_tax_cost').get("amount")/order.get('total_tax_cost').get("divisor"),
                totalPrice= order.get('total_price').get("amount")/order.get('total_price').get("divisor"),
                shopName=shop.name,
            )
            updates.append(convertedOrder.save(orderItems))
        print("updated orders", shopId, len(updates))
        if orders: lastUpdate.save(count=len(updates), timestamp=orders[-1].get('updated_timestamp'))
        return lastUpdate
    except Exception as e:
        print(traceback.format_exc())
        return str(e)

def updateSingleOrder(shop:Shop, platformOrderId: str, readdd=False):
    try:
        shopId=shop.id
        app = Application.get(shop.appId)
        client = EtsyClient(app.apiKey, shop)
        order = getEtsyOrder(client,platformOrderId)
        if order:
            # items = getEtsyOrderItems(client, shop.platformShopId, platformOrderId)
            items = order.get('transactions')
            orderItems = []
            for item in items:
                orderItem = OrderItem(
                    uid=shop.uid,
                    enterpriseId=shop.enterpriseId,
                    platformId=shop.platformId,
                    platformProductId= item.get('listing_id'),
                    platformOrderItemId=item.get('transaction_id'),
                    platformOrderId = platformOrderId,
                    quantity = item.get('quantity'),
                    price = item.get('price', {}).get("amount", 0)/item.get('price', {}).get("divisor", 1),
                    discount = 0,
                    tax=0,
                    subtotal=item.get('price', {}).get("amount", 0)/item.get('price', {}).get("divisor", 1)*item.get('quantity', 1),
                    total=item.get('price', {}).get("amount", 0)/item.get('price', {}).get("divisor", 1)*item.get('quantity',1),
                    variantId= str(item.get('product_id')),
                    name=item.get('title'),
                    shopId=shopId, 
                    createdAt= datetime.fromtimestamp(item.get("created_timestamp", datetime.now().timestamp())),
                    updatedAt= datetime.fromtimestamp(item.get('created_timestamp',  datetime.now().timestamp())),
                    image=Image.from_dict(getListingImage(client,str(item.get("listing_id")),str(item.get("listing_image_id")))),
                    productId=shop.platformId+shop.id+str(item.get("listing_id"))
                )
                variations = item.get('variations', [])
                orderItem.addVariant(
                    Variant(
                        id=item.get('product_id'),
                        price=orderItem.price,
                        sku=item.get("sku"),
                        properties=[
                        Property(
                            name = variation.get('formatted_name'),
                            id = variation.get('property_id'),
                            value = variation.get('formatted_value')
                        ) for variation in variations
                        ]
                    )
                )
                orderItems.append(orderItem)
            shipstationConnection = shop.shipstationConnection
            if shipstationConnection and not (order.get("first_line")):
                shipstationOrder = findShipstationOrder(Shop.get(shop.shipstationConnection.get("shipstationShopId")), orderNumber = str(platformOrderId), storeId = shipstationConnection.get("platformShopId"))
                if shipstationOrder:
                    shippingAddress = shipstationOrder.get("shipTo")
                    order['first_line'] = shippingAddress.get("street1")
                    order['second_line'] = shippingAddress.get("street2")
                    order['city'] = shippingAddress.get("city")
                    order['state'] = shippingAddress.get("state")
                    order['zip'] = shippingAddress.get("postalCode")
                    order['country_iso'] = shippingAddress.get("country")
            convertedOrder = Order(
                uid=shop.uid,
                platformId=shop.platformId,
                shopId=shopId,
                enterpriseId=shop.enterpriseId,
                cancelled=False,
                draft=False,
                shippingAddress=Address(
                    name=order.get('name'),
                    address1= order.get('first_line'),
                    address2= order.get('second_line'),
                    city = order.get('city'),
                    state = order.get('state'),
                    zip = order.get('zip'),
                    country = order.get('country_iso'),
                    countryCode= order.get('country_iso'),
                    email=order.get('buyer_email'),
                ),
                platformOrderId=platformOrderId, 
                createdAt = datetime.fromtimestamp(order.get('created_timestamp')),
                updatedAt= datetime.fromtimestamp(order.get('updated_timestamp')),
                grandTotal= order.get('grandtotal').get("amount")/order.get('grandtotal').get("divisor"),
                shipped= order.get('is_shipped'),
                currencyCode= order.get("grandtotal").get('currency_code'),
                shippingCost= order.get('total_shipping_cost').get("amount")/order.get('total_shipping_cost').get("divisor"),
                totalDiscount= order.get('discount_amt').get("amount")/order.get('discount_amt').get("divisor"),
                totalTax= order.get('total_tax_cost').get("amount")/order.get('total_tax_cost').get("divisor"),
                totalPrice= order.get('total_price').get("amount")/order.get('total_price').get("divisor"),
                shopName=shop.name,
            )
            return dict(id=convertedOrder.save(orderItems, rewrite=True, readd=readdd))
    except Exception as e:
        raise API_Error(str(e))    

def getEtsyShopOrders(client: EtsyClient, platformShopId:str,lastUpdated:int, limit:int =100) -> list:
    url = f"https://openapi.etsy.com/v3/application/shops/{platformShopId}/receipts"
    res = client.get(
        url, 
        params=dict(
            sort_on	= "updated",
            limit=limit,
            min_last_modified = lastUpdated,
            max_last_modified= int(datetime.now(tz=pytz.timezone(client.shop.timezone if client.shop.timezone else "UTC")).timestamp()),
            was_paid=True
        )
    )
    if res.status_code == 200:
        return res.json().get("results", [])
    raise API_Error(res.text, res.status_code)

def getEtsyOrder(client: EtsyClient, platformOrderId: str):
    url = f"https://openapi.etsy.com/v3/application/shops/{client.shop.platformShopId}/receipts/{platformOrderId}"
    res = client.get(url)
    if res.status_code == 200: return res.json()
    return None

def getListingImage(client: EtsyClient, platformProductId:str,imageId: str):
    url = f"https://openapi.etsy.com/v3/application/listings/{platformProductId}/images/{imageId}"
    res = client.get(url)
    if res.status_code == 200: return dict(
        url= res.json().get("url_fullxfull"),
        id= imageId
    )
    return dict(id="", url="")


def getEtsyOrderItems(client: EtsyClient, platformShopId:str,platformOrderId:str) -> list[dict]:
    url = f"https://openapi.etsy.com/v3/application/shops/{platformShopId}/receipts/{platformOrderId}/transactions"
    res = client.get(url)
    if res.status_code == 200: return res.json().get("results", [])
    raise API_Error(res.text, res.status_code)

def getEtsyOrder(client: EtsyClient, platformOrderId:str) -> dict | None:
    url = f"https://openapi.etsy.com/v3/application/shops/{client.shop.platformShopId}/receipts/{platformOrderId}"
    res = client.get(url)
    if res.status_code == 200:
        return res.json()
    print(res.text)
    return None

def submitShipment(shop:Shop, platformOrderId:str, trackingCode:str, carrierName:str):
    app = Application.get(shop.appId)
    client = EtsyClient(app.apiKey,shop)
    url = f"https://openapi.etsy.com/v3/application/shops/{shop.platformShopId}/receipts/{platformOrderId}/tracking"
    res = client.post(
        url=url, 
        json=dict(
            carrier_name=carrierName,
            tracking_code=trackingCode,
            send_bcc=True
        )
    )
    if res.status_code == 200: return True
    print(res.text, res.status_code)
    return False

def submitShipmentForOrder(params:Params):
    orderId = params.args.get("orderId")
    shipment = db.collection("shipments").where("orderId", "==", orderId).get()
    if len(shipment):
        shipment = shipment[0].to_dict()
        order = Order.get(orderId)
        shop = Shop.get(order.shopId)
        app = Application.get(shop.appId)
        client = EtsyClient(app.apiKey,shop)
        url = f"https://openapi.etsy.com/v3/application/shops/{shop.platformShopId}/receipts/{order.platformOrderId}/tracking"
        res = client.post(
            url=url, 
            json=dict(
                carrier_name=shipment.get("carrierName"),
                tracking_code=shipment.get("trackingCode"),
                # send_bcc=True
            )
        )
        if res.status_code == 200: return True
        print(res.text, res.status_code)
        return False

def submitRemainingShipment(params: Params):
    after = datetime(2022, 10, 28)
    orders = [Order.from_dict(doc.to_dict()) for doc in db.collection("orders").where("shippedAt", ">", after).get()]
    orders = [order for order in orders if not order.routed]
    shopIds = set(order.shopId for order in orders)
    shops = {shopId: Shop.get(shopId) for shopId in shopIds}
    apps = {shop.appId:Application.get(shop.appId) for shop in shops.values()}
    for order in orders:
        print(order.id)
        if order.platformId == "1":
            shop:Shop = shops.get(order.shopId)
            app:Application = apps.get(shop.appId)
            client = EtsyClient(app.apiKey, shop)
            res = getEtsyOrder(client, order.platformOrderId)
            if res and not res.get("is_shipped"):
                print(order.platformOrderId, "NOT SHIPPED")
                shipment = db.collection("shipments").where("orderId", "==", order.id).get()
                if len(shipment)>0:
                    shipment = shipment[0].to_dict()
                    print(order.id, submitShipment(shop, order.platformOrderId, shipment.get("trackingCode"), shipment.get("carrierName")))
