from array import array
from msilib.schema import Class
from re import I
import threading
from tkinter.ttk import Separator
from typing import Type
from xmlrpc.client import boolean
from flask import session,Flask,request, jsonify
from flask_restful import Resource, Api
import flask_restful
from itsdangerous import json
from sqlalchemy import delete
from utility.app_logging import logger_name
import logging
import persistence
import Model.isp_model as isp_model
import Model.model_manager as model_manager
from werkzeug.security import check_password_hash
import anytree
import View.view_privilege as privileges
from datetime import timedelta
from flask_session import Session
import View.view_error_management as view_error_management
logger = logging.getLogger(logger_name + ".VIEW")



__app__ = Flask("OpenIsp")
__app__.secret_key = "aseqzdwxc"
__app__.permanent_session_lifetime = timedelta(minutes=2)
__app__.config["SESSION_PERMANENT"] = False
__app__.config["SESSION_TYPE"] = "filesystem"


__resource_array__ : array


__id_counter__ : int = 1




from werkzeug.serving import make_server
class ServerThread(threading.Thread):

    def __init__(self, app,ip,port):
        threading.Thread.__init__(self)
        self.server = make_server(ip, port, app)
        self.ctx = app.app_context()
        self.ctx.push()

    def run(self):
        logger.info('starting server')
        self.server.serve_forever()

    def shutdown(self):
        self.server.shutdown()

__server_process__ : ServerThread

def get_roles_ids() :
    return session["roles_ids"]


def init() :

    @__app__.before_request
    def before_request_func():
        print(request.headers.__dict__)
        global __id_counter__
        logger.debug("before_request is running!")
        if not "client_id" in session :
            session["client_id"] = str(__id_counter__)
        logger.debug("client_id is " + session["client_id"])
        __id_counter__ = __id_counter__ + 1


        if not request.path == "/api/login" and not "username" in session :
            logger.warning("Unauthorized client with id " + session["client_id"] + " try to access application")
            resp = jsonify({'message' : 'Unauthorized'})
            resp.status_code = 401
            return resp

        if "username" in session :
            logger.debug("request from  " + session["username"])





    from flask.logging import default_handler
    __app__.logger.removeHandler(default_handler)


    privileges.init()
    view_error_management.define_error_management(__app__)

@__app__.route('/api/login',methods = ['POST'])
def login():
    _json = request.json
    _username = _json['username']
    _password = _json['password']


    with persistence.get_Session_Instance() as sess :

        Item = sess.query(isp_model.user_account).filter(isp_model.user_account.nickname == _username).first()
        if not isinstance(Item,isp_model.user_account) :
            logger.warning("user tried to login with unknown account name : " + _username)
            resp = jsonify({'message' : 'Bad Request - user account not found'})
            resp.status_code = 400
            return resp

        if not check_password_hash(Item.password,_password) :
            logger.warning("user with account name '" + _username + "' tried to login with invalid password")
            resp = jsonify({'message' : 'Bad Request - invalid password for this account'})
            resp.status_code = 400
            return resp



        session["username"] = _username
        session["account_data"] = model_manager.ModelObjectToJsonString(Item)
        session["roles_ids"] = [privileges.inventory_read_only_role.id]
        logger.info("account " + _username + " logged IN successfully with id : " + session["client_id"])
        resp = jsonify({'message' : 'login successful'})
        resp.status_code = 200
        return resp


@__app__.route('/api/logout',methods = ['POST'])
def logout():
    logger.info("account " + session["username"] + " logged OUT with id : " + session["client_id"])
    session.clear()
    return jsonify('logout')


@__app__.route('/routes',methods = ['GET'])
@privileges.manager.require_authorization(required_role=privileges.inventory_admin_role,ids_getter=get_roles_ids)
def routes():
    routes = []
    for route in __app__.url_map.iter_rules():
        routes.append('%s' % route)
    return jsonify(routes)

def run() :
    global __server_process__
    __server_process__ = ServerThread(__app__,"0.0.0.0",8000)
    __server_process__.start()
    logger.info('View server started')



def stop() :
    global __server_process__
    __server_process__.shutdown()
    logger.info('View server stopped')



class ResourceNode(anytree.NodeMixin) :
    def __init__(self, url_key : str, resource = None, parent=None, children=None):
        super(ResourceNode, self).__init__()
        self.url_key = url_key
        self.parent = parent
        if children:
            self.children = children

        if resource :
            self.resource = resource


            # we check if the resource class has an http API method
            get    = getattr(self, "get",    None)
            post   = getattr(self, "post",   None)
            delete = getattr(self, "delete", None)
            update = getattr(self, "update", None)
            patch  = getattr(self, "patch",  None)

            if  not callable(get)    and not callable(post) \
            and not callable(delete) and not callable(update) \
            and not callable(patch) :
                raise BaseException("class does have an api class")


    url_slice = None
    resource = None


def add_resource_tree(node : ResourceNode) :

    if not node.is_root() :
        raise BaseException("node is not a root node")

    logger.debug("adding resource tree for API")
    logger.debug(anytree.RenderTree(node, style=anytree.AsciiStyle()).by_attr("url_slice"))

    for node in anytree.PreOrderIter(node) :
        logger.debug("iterating though node " + node.url_key)

        if node.resource is not None :
            logger.debug("resource found")

            url = ""
            parent : ResourceNode
            for parent in node.ancestors :
                url = url + node.separator + parent.url_key

            print("url built for the node : " + url)

            __api__.add_resource(node.resource, url)
            __resource_array__.append(node)

#node.ancestors ( all parent )

def delete_resource_tree(node : ResourceNode, cascade : boolean = True) :

    if not cascade and not node.is_leaf() :
        raise BaseException("cannot delete only a node when it's not a leaf !")


    raise BaseException("Not implemented yet") #TODO



