import json
import traceback
from typing import Callable

from flask import Request, Response

from configs.firebase import SERVER_TIMESTAMP, db
from V2.functions.Users import main as Users
from V2.Params import Params, PublicParams

mimetype='application/json'
post_headers = {"Access-Control-Request-Methods": "POST, GET, OPTIONS, DELETE, PUT", "Access-Control-Request-Headers": "Content-Type, Authorization, x-uid, x-api-key,X-Api-Key, X-Uid", 'Access-Control-Allow-Origin': '*', "Content-Type":mimetype}

class PrintifyError(Exception):
    def __init__(self, status_code=500,requestId=None, **kwargs) -> None:
        ''' New Error Instance '''
        self.status_code = int(status_code)
        self.kwargs = kwargs
        self.requestId = requestId
        super().__init__(self.to_dict())

    def to_dict(self) -> dict:
        ''' dict Error object '''
        return dict(
            **self.kwargs
        )
    def to_json(self) -> str:
        ''' JSON serializable Error object '''
        return json.dumps(self.to_dict())
    
    def response(self) -> Response:
        return Response(self.to_json(), status=self.status_code, headers=post_headers, mimetype=mimetype)


class API_Error(Exception):
    def __init__(self, message:str, status_code=500, meta=None) -> None:
        ''' New Error Instance '''
        self.message = message
        self.status_code = int(status_code)
        self.meta = meta if meta else {}
        super().__init__(self.to_dict())

    def to_dict(self) -> dict:
        ''' dict Error object '''
        return dict(
            message=self.message,
            status_code = self.status_code,
            meta=self.meta
        )
    def to_json(self) -> str:
        ''' JSON serializable Error object '''
        return json.dumps(self.to_dict())
    
    def response(self) -> Response:
        return Response(self.to_json(), status=self.status_code, headers=post_headers, mimetype=mimetype)


def sendResponse(function: Callable[[Params], dict | list], request: Request, id=None, authRequired=True) -> Response:
    try:
        if request.method == "OPTIONS": return Response("", status=200, headers=post_headers, mimetype=mimetype)
        parameters = dict(request.args.to_dict())
        parameters.update(request.json if request.json else {})
        parameters.update(dict(request.headers))
        uid = request.headers.get('x-uid',request.headers.get('X-Uid'))
        currentUser = Users.getUser(uid)
        if not currentUser and authRequired: raise API_Error("Authentication failed.", 401)
        params = Params(
            currentUser=currentUser,
            hostname=request.headers.get('domain-hostname', "https://app.riverr.app"),
            id=id if id else parameters.get('id'),
            args=parameters
        )
        data  = function(params)
        return Response(json.dumps(data, default=str), 200, post_headers, mimetype=mimetype)
    except API_Error as e:
        print(traceback.format_exc())
        return e.response()
    except Exception as e:
        print(traceback.format_exc())
        return Response(json.dumps(dict(status=500, message='Internal Server Error', meta={"error": str(e)})), status=500, headers=post_headers, mimetype=mimetype)

def saveError(uid:str, functionName:str, userMessage:str, **kwargs) -> None:
    error = dict(
        createdAt=SERVER_TIMESTAMP,
        uid=uid,
        userMessage=userMessage,
        functionName=functionName,
        server='python'
    )
    error.update(kwargs)
    db.collection("LOGS").add(error)

def priorityLogger(uid:str, functionName:str, userMessage:str,serverMessage=None, **kwargs) -> None:
    error = dict(
        createdAt=SERVER_TIMESTAMP,
        uid=uid,
        userMessage=userMessage,
        functionName=functionName,
        server='python',
        serverMessage=serverMessage
    )
    error.update(kwargs)
    db.collection("PRIORITY_LOGS").add(error)
    print("ERROR LOGGED",uid, functionName, userMessage)



def sendPublicResponse(function: Callable[[PublicParams], dict | list], request: Request, id=None) -> Response:
    try:
        if request.method == "OPTIONS":
            return Response("", status=200, headers=post_headers, mimetype=mimetype)
        parameters = dict(request.args.to_dict())
        parameters.update(request.json if request.json else {})
        domainhostname = request.headers.get("domain-hostname")
        try: enterprise = Users.getEnterpriseByHostname(domainhostname)
        except: enterprise=None
        params = PublicParams(
            enterprise=enterprise,
            hostname=request.headers.get('domain-hostname', "https://app.riverr.app"),
            id=id if id else parameters.get('id'),
            args=parameters,
            headers=dict(request.headers)
        )
        data  = function(params)
        return Response(json.dumps(data, default=str), 200, post_headers, mimetype=mimetype)
    except API_Error as e:
        print(traceback.format_exc())
        return e.response()
    except Exception as e:
        print(traceback.format_exc())
        return Response(json.dumps(dict(status=500, message='Internal Server Error', meta={"error": str(e)})), status=500, headers=post_headers, mimetype=mimetype)
