
import json
import traceback
from datetime import datetime

import requests

from configs.firebase import SERVER_TIMESTAMP, db
from functions.Enterprises import getItemCost
from functions.LastUpdated import productsLastUpdated, saveProductsLastUpdated
from functions.Products import (convertImage, convertProduct, convertProperty,
                                convertVariant, getVariantMapping, saveProduct)
from functions.products.SubmitProduct import connectionVariantImage
from functions.Response import API_Error, saveError
from functions.Shopify.Auth import apiVersion, createHeader
from functions.Shopify.Fulfillment import (connectionInventoryLocation,
                                           createFulfillmentService,
                                           getFulfillmentService,
                                           getFulfillmentServices)
from functions.Shops import getShopById, getShops
from functions.Suppliers.BlankProducts import removehtml, worker


def updateAllProducts(params={}):
    shops = [shop for shop in getShops(platformId="2", credentials= True) if not shop.get("disabled")]
    res =  worker(updateShopProducts, shops)
    return str(res)

def updateShopProducts(shop):
    if shop:
        updates = []
        uid = shop.get('uid')
        shopId = shop.get('id')
        accessToken = shop.get('accessToken')
        url = shop.get('url')
        headers = createHeader(accessToken)
        productsUrl = f"{url}/admin/api/{apiVersion}/products.json"
        lastUpdate = productsLastUpdated(shopId)
        updated_at_min= datetime.fromtimestamp(lastUpdate.get('timestamp')).isoformat()
        lastOrderTimestamp = lastUpdate.get('timestamp')
        endpoint = lastUpdate.get('endpoint')
        try:
            if endpoint:
                res = requests.get(endpoint, headers=headers)
            else:
                res = requests.get(productsUrl, headers=headers, params=dict(updated_at_min= updated_at_min if lastUpdate.get("existed") else None, limit=250, published_status= "published"))
            if res.status_code != 200:
                print(res.text, res.status_code, shopId)
                return res.json()
            products =  []
            nextPage = None
            endpoint = None
            if res.status_code == 200:
                products.extend(res.json().get('products', []))
                Link = res.headers.get("Link")
                if Link:
                    nextPage = Link.endswith('rel="next"')
                    while Link and nextPage:
                        endpoint = Link.replace("<", "").replace('>; rel="next"', "")
                        if len(products)>500:
                            break
                        request = requests.get(endpoint, headers=headers)
                        if request.status_code == 200:
                            data = request.json()
                            products.extend(data.get('products', []))
                            Link = request.headers.get("Link")
                            nextPage = Link.endswith('rel="next"')
                        else:
                            print(request.text)
                            saveError(uid, "Shopify/Products/updateShopProducts", res.text,error=dict(
                                    shopId=shopId,
                                    lastUpdate=lastUpdate,updated_at_min=updated_at_min
                                ))
                            break
                for product in products:
                    try:
                        description = removehtml(product.get('body_html','')) if product.get('body_html') else ""
                    except Exception as e:
                        description = ""
                        print(e)
                    convertedProduct = convertProduct(
                            uid=uid,
                            shopId = shopId,
                            platformId = shop.get('platformId'),
                            enterpriseId= shop.get('enterpriseId'),
                            platformProductId = product.get('id'),
                            name = product.get('title'),
                            description= description,
                            createdAt = datetime.fromisoformat(product.get('created_at')),
                            updatedAt= SERVER_TIMESTAMP,
                            tags = product.get('tags', "").split(','),
                            price= product.get('price'),
                            images = [convertImage(url = image.get('src'), primary= image.get('position') == 1, id = image.get('id')) for image in product.get('images', [])]
                        )
                    variants = product.get('variants')
                    options = product.get('options')
                    convertedVariants = []
                    for variant in variants:
                        properties =  [convertProperty(name = option.get('name'), id = option.get('id'), value = variant.get(f'option{option.get("position")}')) for option in options]
                        convertedVariant = convertVariant(
                            id = variant.get('id'),
                            price = variant.get('price'),
                            # ignore sku for app.myshirtpod.com
                            sku = None if shop.get('enterpriseId') == "lJ2eUFov5WyPlRm4geCr" else  variant.get('sku'),
                            properties=properties,
                            inventory_item_id = variant.get('inventory_item_id'),
                        )
                        convertedVariants.append(convertedVariant)
                    updates.append(saveProduct(convertedProduct, convertedVariants))
                saveProductsLastUpdated(shopId, len(products), lastPage=nextPage, nextPage = nextPage, total = len(products), timestamp=lastOrderTimestamp if nextPage and endpoint else None,
                    endpoint=endpoint if nextPage else None
                )
                return f"Products Updated => {len(updates)}"
            else:
                print(res.text)
                saveError(uid, "Shopify/Products/updateShopProducts", res.text,error= dict(
                    shopId=shopId,
                    lastUpdate=lastUpdate,updated_at_min=updated_at_min
                ))
            return (res.text)
        except Exception as e:
            print(e)
            saveError(uid, "Shopify/Products/updateShopProducts", str(e),error= dict(
                shopId=shopId,
                lastUpdate=lastUpdate
            ))
            return str(e)
    return None

def connectProductToLocationAfterMapping(data:dict):
    shopId = data.get('shopId')
    productId =     data.get('productId')
    platformProductId = data.get('platformProductId')
    shop = getShopById(shopId, True)
    enterpriseId = shop.get('enterpriseId')
    accessToken = shop.get('accessToken')
    url = shop.get('url')
    headers = createHeader(accessToken)
    fulfillmentService = getFulfillmentService(shopId)
    if not fulfillmentService:
        fulfillmentService = createFulfillmentService(dict(
            shopId = shopId,
            shop=shop,
            currentUser = data.get('currentUser'),            
        ))
    variants = getShopifyProduct(url, platformProductId, headers)
    if variants:
        for variant in variants:
            inventory_item_id = variant.get('inventory_item_id')
            variantMapping = getVariantMapping(productId, str(variant.get('id')))
            if not variant.get("sku"):
                print("No sku")
                print("generated sku", variantMapping.get("sku"))
                updateVariantSku(url, headers, variant.get('id'), variantMapping.get("sku"))
            if enterpriseId != "5f9f1b0b0e2b4b0017e1b0a5": locationConnected = connectionInventoryLocation(url, headers, inventory_item_id, fulfillmentService.get('location_id'))
            cost = None
            try: cost = getItemCost(enterpriseId, variantMapping.get('blankProductId'), variantMapping.get('blankVariantId'))
            except Exception as e:print(traceback.format_exc())
            if type(cost) not in [int,float]: cost = 0
            costSubmitted = updateItemCostOnShopify(url, headers, inventory_item_id,cost)
            if not (locationConnected or costSubmitted): print("locationConnected", locationConnected,"costSubmitted",costSubmitted)
    return []


def updateVariantSku(url, headers, variantId, sku):
    url = f'{url}/admin/api/{apiVersion}/variants/{variantId}.json'
    data = dict(variant=dict(id=variantId,sku=sku))
    res = requests.put(url, data=json.dumps(data), headers=headers)
    print(res.text)
    if res.status_code == 200: return True
    raise False

def getShopifyProduct(url, platformProductId, headers):
    productsUrl = f"{url}/admin/api/{apiVersion}/products/{platformProductId}/variants.json"
    res = requests.get(productsUrl, headers=headers)
    if res.status_code == 200:
        product = res.json()
        return product.get("variants")
    return None

def updateItemCostOnShopify(url, headers, itemId, cost):
    url = f'{url}/admin/api/{apiVersion}/inventory_items/{itemId}.json'
    data = dict(inventory_item=dict(cost=cost))
    res = requests.put(url, data=json.dumps(data), headers=headers)
    if res.status_code == 200: return True
    return False

def updateSingleProduct(productId, shop):
    accessToken = shop.get('accessToken')
    uid = shop.get('uid')
    shopId = shop.get('id')
    accessToken = shop.get('accessToken')
    url = shop.get('url')
    headers = createHeader(accessToken)
    productsUrl = f"{url}/admin/api/{apiVersion}/products/{productId}.json"
    try:
        res = requests.get(productsUrl, headers=headers)
        if res.status_code != 200:
            print(res.text, res.status_code, shopId)
            return res.json()
        if res.status_code == 200:
            product = res.json().get('product')
            print(product)
            try:
                description = removehtml(product.get('body_html','')) if product.get('body_html') else ""
            except Exception as e:
                description = ""
                print(e)
            convertedProduct = convertProduct(
                uid=uid,
                shopId = shopId,
                platformId = shop.get('platformId'),
                enterpriseId= shop.get('enterpriseId'),
                platformProductId = product.get('id'),
                name = product.get('title'),
                description= description,
                createdAt = datetime.fromisoformat(product.get('created_at')),
                updatedAt= SERVER_TIMESTAMP,
                    tags = product.get('tags', "").split(','),
                    price= product.get('price'),
                    images = [convertImage(url = image.get('src'), primary= image.get('position') == 1, id = image.get('id')) for image in product.get('images', [])]
                )
            variants = product.get('variants')
            options = product.get('options')
            convertedVariants = []
            for variant in variants:
                properties =  [convertProperty(name = option.get('name'), id = option.get('id'), value = variant.get(f'option{option.get("position")}')) for option in options]
                convertedVariant = convertVariant(
                    id = variant.get('id'),
                    price = variant.get('price'),
                    sku = variant.get('sku'),
                    properties=properties,
                    inventory_item_id = variant.get('inventory_item_id'),
                )
                convertedVariants.append(convertedVariant)
            return saveProduct(convertedProduct, convertedVariants)
        return productId
    except Exception as e:
        return str(e)

def shopifyCustomizer(params:dict):
    product,variations, placementDetails = params.get('product').get("productDetails"),params.get('product').get("variations"), params.get('placementDetails')
    uid, enterpriseId = params.get("currentUser").get("uid"), params.get("currentUser").get("enterpriseId")
    shopname = params.get('shopname')
    shopId = product.get("shopId")
    if product.get("shopId"): shop = db.collection("shopsCredentials").document(shopId).get().to_dict()
    else:
        if shopname == "localhost": shop = db.collection("shopsCredentials").document("1iILIxJA5dtlQzEjnaDU").get().to_dict()
        else: 
            shop = db.collection("shopsCredentials").where("url", "in", [shopname, f"https://{shopname}"]).get()
            if len(shop) == 0: 
                raise API_Error(f"Shop not found {shopname}")
            shop = shop[0].to_dict()
    shopId = shop.get("id")
    productId = product.get('id')
    variantsSku = {variant.get('sku'):variant.get('id') for variant in variations}
    varIds = {variant.get("sku"): variant.get("varId") for variant in variations}
    colors = [v.get('color') for v in variations]
    sizes = [v.get('size') for v in variations]
    type = product.get("type")
    # priceRules = params.get("priceRules", [])
    # for p in priceRules:
    options = [
            dict(
                name="Color",
                values=colors
            ),
            dict(
                name="Size",
                values=sizes
            ),
            ]
    variants = [dict(option1=v.get("color"), option2=v.get("size"),price=v.get("price"), sku=v.get("sku")) for v in variations]
    if type == "nester":
        options = [
            dict(
                name="Size",
                values=sizes
            ),
            dict(
            name="Sheet #",
            values= [str(i) for i in range(1, len(variations)+1)]
        ),
        ]
        variants = [dict(option1=v.get("size"),option2=variations.index(v)+1, price=v.get("price"), sku=v.get("sku")) for v in variations]
    # print(variants, options)
    previewImages = {v.get("sku"):v.get("selectedPreview",{}).get("url") for v in variations if v.get("selectedPreview")}
    # print(previewImages)
    # return previewImages
    name = product.get('name')
    description = product.get('description')
    tags = product.get('tags')
    tags.append("hidden-from-recommendations")
    decorationType = params.get('product').get("decorationType")
    # shopId = product.get('shopId')
    # shop = getShopById(shopId, True)
    url = shop.get("url")
    listing_url = f"{url}/admin/api/{apiVersion}/products.json"
    headers = {'X-Shopify-Access-Token': shop.get("accessToken"), "Content-Type": "application/json"}
    product = dict(
        title=name,
        body_html=description,
        published=True,
        tags=tags,
        options=options,
        variants=variants,
        product_type = "POD Customizer",
        # images = [dict(src = image) for image in previewImages],
        decorationType=decorationType
    )
    # print(product)
    request = requests.post(listing_url,data=json.dumps(dict(product=product)),  headers=headers)
    if request.status_code == 201:
        listing = request.json().get("product")
        platformProductId = str(listing.get("id"))

        metafields_url = f"{url}/admin/api/{apiVersion}/products/{platformProductId}.json"

        # print(metafields_url)

        metafield_data = {
            "product": {
                "id": platformProductId,
                "metafields": [
                    {
                        "key": "hidden",
                        "value": "1",
                        "type": "number_integer",
                        "namespace": "seo"
                    }
                ]
            }
        }

        metafield_response = requests.put(metafields_url, json=metafield_data, headers=headers)

        if metafield_response.status_code == 201:
            print("Metafield created successfully to hide product from search.")
        else:
            print("Failed to create metafield:", metafield_response.json())
            

        items = listing.get('variants')
        collectionId = getSmartCollection(shopId)
        if not collectionId: collectionId = createSmartCollection(url, headers, shopId)
        # print(collectionId, "COLLECTION")
        # addProductToCustomCollection(url, headers, platformProductId, collectionId)
        variantIds = {variant.get('sku'):variant.get('id') for variant in items}
        fulfillmentService = getFulfillmentService(shopId)
        if not fulfillmentService:
            fulfillmentService = createFulfillmentService(dict(shopId=shopId,currentUser = dict(
                uid = shop.get('uid'),
                enterpriseId=enterpriseId,
                shop=shop
            )))
        for item in items:
            inventory_item_id = item.get("inventory_item_id")
            connectionInventoryLocation(url, headers, inventory_item_id, fulfillmentService.get('location_id'))
            # print(item)
            updateItemCostOnShopify(url, headers, inventory_item_id, item.get('price'))
            # print(previewImages)
            connectionVariantImage(url, headers, src = previewImages.get(item.get('sku')), productId=platformProductId, variantIds=[item.get("id")])
        variants = [dict(platformVariantId=v.get("id"),blankVariantId = variantsSku.get(v.get("sku")),sku=v.get('sku'),varId=varIds.get(v.get("sku")), id=v.get('sku'), platformProductId=platformProductId, price=v.get("price"), productId=productId) for v in listing.get('variants')]
    else:
        data = request.json()
        raise API_Error(str(data.get("errors", {}), request.status_code))
    # placementDetails['product'] = platformProductId
    # placementDetails['variants'] = variants
    # placementDetails['parentProductId'] = productId
    _, ref = db.collection("shopifyCartPlacements").add(dict(
        placementDetails=placementDetails,
        product=product,
        parentProductId = productId,
        enterpriseId=enterpriseId,
        uid= uid,
        createdAt = SERVER_TIMESTAMP,
        updatedAt = SERVER_TIMESTAMP,
        shopId = shopId,
        variants=variants,
        variations = variations,
        type=type
    ))
    return dict(platformProductId=platformProductId, variants=[{
        **variant, "varId": varIds.get(variant.get("sku"))
    } for variant in variants], placementsDocumentId=ref.id)

# def createCustomCollection(url, headers, shopId):
#     collection_url = f"{url}/admin/api/2022-04/custom_collections.json"
#     collection = dict(
#         custom_collection = dict(
#             title = "POD Customizer",
#             published = False
#         )
#     )
#     request = requests.post(collection_url, data=json.dumps(collection), headers=headers)
#     if request.status_code == 201:
#         collection =  dict(request.json().get("custom_collection"))
#         id = saveCustomCollection(shopId, collection)
#         return id
#     else:
#         data = request.json()
#         raise API_Error(str(data.get("errors", {}).values()), request.status_code)

def createSmartCollection(url, headers, shopId):
    collection_url = f"{url}/admin/api/{apiVersion}/smart_collections.json"
    collection = dict(
        smart_collection = dict(
            title = "POD Customizer Collection",
            rules= [
                {
                    "column": "type",
                    "relation": "equals",
                    "condition": "POD Customizer"
                },
            ],
            published=False
        )
    )
    request = requests.post(collection_url, data=json.dumps(collection), headers=headers)
    if request.status_code == 201:
        collection =  dict(request.json().get("smart_collection"))
        collectionId = saveSmartCollection(shopId, collection)
        collection = dict(
            smart_collection = dict(
                title = "All",
                rules= [
                    {
                        "column": "type",
                        "relation": "not_equals",
                        "condition": "POD Customizer"
                    },
                ],
                published=True
            )
        )
        requests.post(collection_url, data=json.dumps(collection), headers=headers)
        return collectionId
    else:
        raise API_Error(request.text, request.status_code)

# def getCustomCollection(shopId):
#     ref = db.collection("shopifyCustomCollections").document(shopId).get()
#     if ref.exists: return ref.to_dict().get("collectionId")
#     return None

def getSmartCollection(shopId):
    ref = db.collection("shopifySmartCollections").document(shopId).get()
    if ref.exists: return ref.to_dict().get("collectionId")
    return None

def saveSmartCollection(shopId, collection:dict):
    collection['collectionId'] = collection.get('id')
    collection['id'] = shopId
    db.collection("shopifySmartCollections").document(shopId).set(collection)
    return collection.get('collectionId')

# # add production to custom collection
# def addProductToCustomCollection(url, headers, productId, collectionId):
#     collection_url = f"{url}/admin/api/2022-04/collects.json"
#     collect = dict(product_id=productId, collection_id=collectionId)
#     request = requests.post(collection_url, data=json.dumps(dict(collect=collect)), headers=headers)
#     if request.status_code == 201:
#         print(request.text)
#         return True
#     raise API_Error(request.text, request.status_code)