import base64
import datetime
import json
import traceback
from typing import Callable

from flask import Blueprint, Request, Response, request

from configs.firebase import db
from V2.functions.Orders.Update import updateSingleOrder
from V2.functions.Printify import Orders, Params, Products
from V2.functions.Printify.Events import printifyQc, sendPrintifyEventEndpoint
from V2.functions.Users.main import User, getUser
from V2.middlewares.auth import (PrintifyError, mimetype, post_headers,
                                 sendResponse)

printifyBlueprint = Blueprint("printify", __name__)

PRINTIFY_ENTERPRISE_ID = "FJQsdKRMyURH8efVSFzk"
PRINTIFY_SUPER_ADMIN = "IH0yzaQsDgVnHA47tKsuyqiJ3Vh2"

import pytz


def base64Decode(string:str)->str:
    base64_bytes = string.encode("ascii")
    sample_string_bytes = base64.b64decode(base64_bytes)
    decoded_string = sample_string_bytes.decode("ascii")
    return decoded_string


def savePrintifyRequest(data:dict):
    _, ref = db.collection("PrintifyRequests").add(data)
    return ref.id

def sendPrintifyResponse(function: Callable[[Params.PrintifyParams], dict | list], request: Request, id=None, **kwargs) -> Response:
    requestId = str(int(datetime.datetime.now().timestamp()))
    try:
        if request.method == "OPTIONS": return Response("", status=200, headers=post_headers, mimetype=mimetype)
        data = {}
        try: data = dict(request.json) if request.json and request.is_json else None
        except Exception as e: print(e)
        if type(data) != dict or not data: data = {}
        requestId = savePrintifyRequest(dict(
            createdAt=datetime.datetime.now(pytz.utc),
            path=request.full_path,
            data=data,
            method=request.method,
            headers=dict(request.headers)
        ))
        parameters = dict(**request.args.to_dict(), **kwargs)
        parameters.update(data)
        apiKey = request.headers.get('X-API-KEY',request.headers.get('x-api-key', ''))
        if not apiKey: raise PrintifyError(status_code = 401, errors=[dict(
            id=id,
            message="Authentication Failed."
        )])
        if apiKey: apiKey = str(apiKey)
        uid, locationId = base64Decode(apiKey).split("-@-")
        currentUser = getUser(uid)
        if not currentUser:
            raise PrintifyError(status_code = 401, errors=[dict(
            id=id,
            message="Authentication Failed."
        )])
        location = Params.Location.get(currentUser.enterpriseId, locationId)
        params = Params.PrintifyParams(
            currentUser=currentUser,
            hostname=request.headers.get('domain-hostname', "https://printify.myriverr.com"),
            id=id,
            args=parameters,
            location=location
        )
        post_headers['Riverr-RequestId'] = requestId
        data = function(params)
        if type(data) == tuple:
            data, status = data
            data = {key:value for key,value in dict(data).items() if key and value}
            if not data: return Response(status=204,headers=post_headers)
            return Response(json.dumps(data, default=str), status, post_headers, mimetype=mimetype)
        return Response(json.dumps(data, default=str), 200, post_headers, mimetype=mimetype)
    except PrintifyError as e:
        e.requestId = requestId
        print(traceback.format_exc())
        db.document(f"PrintifyRequests/{requestId}").set(dict(
            error = traceback.format_exc(),
            response = e.to_dict()
        ), merge=True)
        return e.response()
    except Exception as e:
        print(traceback.format_exc())
        res = dict(
            error = traceback.format_exc(),
            response = dict(status="failed", message='Internal Server Error', errors = [
            dict(
                message="Server Error ocurred.",
                requestId = requestId
            )
        ]))
        db.document(f"PrintifyRequests/{requestId}").set(res, merge=True)
        return Response(json.dumps(res), status=500, headers=post_headers, mimetype=mimetype)


@printifyBlueprint.post("/orders.json")
def submitProductionOrder(): return sendPrintifyResponse(Orders.submitProductionOrder,request)

@printifyBlueprint.post("/facilities/<locationId>/orders.json")
def submitProductionOrderToLocation(locationId): return sendPrintifyResponse(Orders.submitProductionOrder,request, id=locationId)

@printifyBlueprint.get("/orders/<printifyOrderId>.json")
def getPrintifyOrder(printifyOrderId): return sendPrintifyResponse(Orders.getPrintifyOrder,request, id=printifyOrderId)

@printifyBlueprint.get("/order/<printifyOrderId>/events.json")
def getPrintifyOrderEvents(printifyOrderId): return sendPrintifyResponse(Orders.getPrintifyOrderEvents,request, id=printifyOrderId)

@printifyBlueprint.put("/order/<printifyOrderId>.json")
def updatePrintifyOrder(printifyOrderId): return sendPrintifyResponse(Orders.updatePrintifyOrder,request, id=printifyOrderId)

@printifyBlueprint.post("/order/<printifyOrderId>/cancel.json")
def cancelPrintifyOrder(printifyOrderId): return sendPrintifyResponse(Orders.cancelPrintifyOrder,request, id=printifyOrderId)

@printifyBlueprint.get("/stock.json")
def getPrintifyStock(): return sendPrintifyResponse(Products.getPrintifyStock,request)

@printifyBlueprint.get("/stock/<sku>.json")
def getPrintifyProductStock(sku): return sendPrintifyResponse(Products.getPrintifyProductStock,request, sku=sku)

@printifyBlueprint.get("/facilities/<locationId>/stock/<sku>.json")
def getPrintifyStockAtLocation(locationId,sku): return sendPrintifyResponse(Products.getPrintifyStockAtLocation,request, id=locationId, sku=sku)

@printifyBlueprint.post("/events/send")
def sendPrintifyEvents(): return sendResponse(sendPrintifyEventEndpoint,request)

@printifyBlueprint.post("/aqc/orders/<printifyOrderId>/items/<itemId>/results")
def receivePrintifyQc(printifyOrderId, itemId): return sendPrintifyResponse(printifyQc,request, printifyOrderId=printifyOrderId, itemId=itemId)

@printifyBlueprint.post("/aqc/orders/<printifyOrderId>/items/<itemId>/barcodes/<barcodeId>/results.json")
def receivePrintifyQcNew(printifyOrderId, itemId, barcodeId): return sendPrintifyResponse(printifyQc,request, printifyOrderId=printifyOrderId, itemId=itemId, barcodeId=barcodeId)