__doc__ = """
    arguments :
        --config    path of the configuration otherwise it will look for the default in the working directory
"""
from flask import Flask,render_template,send_from_directory,request, redirect, Response, session
import flask
import transport
from transport import providers
import cms
import sys
import os
import json
import copy
import io
import base64
from jinja2 import Environment, BaseLoader
import typer
from typing_extensions import Annotated
from typing import Optional


import pandas as pd
import uuid
import datetime
import requests

from cms import disk, cloud, engine

_app = Flask(__name__)
cli = typer.Typer()
@_app.route('/<id>/favicon.ico')
def favicon(id):
    global _route
    # _system = _route.get ().system()
    # _handler = _route.get()
    _handler = _getHandler(id)
    _system = _handler.system()
    _logo =_system['icon'] #if 'icon' in _system else 'static/img/logo.svg'
    _stream = requests.get(''.join([request.host_url,_logo]))
    
    return "_stream",200,{"Content-Type":"image/png"} #_handler.get(_logo),200,{"content-type":"image/png"}
  
def _getHandler (app_id,resource=None) :
    global _route
    _id = _getId(app_id,resource)
    
    return _route._apps[_id]
def _getId(app_id,resource):
    return '/'.join([app_id,resource]) if resource else app_id
def _setHandler (app_id,resource) :
    session['app_id'] = _getId(app_id,resource)

@_app.route("/robots.txt")
def robots_txt():
    """
    This function will generate a robots expression for a variety of crawlers, the paths will be provided by
    menu options
    """
    global _route
    _system = _route.get ().system()

    _info  = ['''
    User-agent: *
    Allow: /
    ''']

    if 'routes' in _system  :
        for _key in _system['routes'] :
            _uri = '/'.join(['',_key])
            _info.append(f'''
    User-agent: *
    Allow: {_uri}
                        ''')
    
    # return '\n'.join(_info),200,{'Content-Type':'plain/text'}
    return Response('\n'.join(_info), mimetype='text/plain')  
def _getIndex (app_id ,resource=None):
    _handler = _getHandler(app_id,resource)
    _layout = _handler.layout()
    _status_code = 200
    global _route
    _index_page = 'index.html'
    _args = {}
    
    try :
        _uri = os.sep.join([_layout['root'],_layout['index']])
        _id = _getId(app_id,resource)
        _args = _route.render(_uri,'index',_id)
        
    except Exception as e:
        _status_code = 404
        _index_page = '404.html'
        print(e)
    return render_template(_index_page,**_args),_status_code
@_app.route("/")
def _index ():
    return _getIndex('main')
# def _xindex ():
#     _handler = _getHandler()
#     _config = _handler.config()
#     global _route

#     # print ([' serving ',session.get('app_id','NA'),_handler.layout()['root']])
#     _args={'system':_handler.system(skip=['source','app','data']),'layout':_handler.layout()}
    
#     try:
#         uri = os.sep.join([_config['layout']['root'], _config['layout']['index']])        
#         _index_page = "index.html"
        
#         _args = _route.render(uri,'index',session.get('app_id','main'))
#         # _setHandler('main')
#     except Exception as e:
#     #     print ()
#         print (e)
#         _index_page = "404.html"
    
#     return render_template(_index_page,**_args),200 if _index_page != "404.html" else 200
@_app.route("/<app>/<resource>")
@_app.route("/<app>",defaults={'resource':None})
def _aindex (app,resource=None):
    _handler = _getHandler(app,resource)
    _setHandler(app,resource)
    _html,_code = _getIndex(app,resource)
    
    return _html,_code
# @_app.route('/id/<uid>') 
# def people(uid):
#     """
#     This function will implement hardened links that can directly "do something"
#     """
#     global _config    
#     return "0",200
  
@_app.route('/dialog')
def _dialog ():
    # global _config 
    global _route
    _handler = _route.get()
    _uri = request.headers['uri']
    
    _id = request.headers['dom']
    # _html = ''.join(["<div style='padding:1%'>",str( e.render(**_args)),'</div>'])
    _args = _route.render(_uri,'html',session.get('app_id','main'))
    _args['title'] = _id
    return render_template('dialog.html',**_args) #title=_id,html=_html)

@_app.route("/api/<module>/<name>",defaults={'app':'main','key':None})
@_app.route("/<app>/api/<module>/<name>",defaults={'key':None})
@_app.route("/<app>/<key>/<module>/<name>",defaults={'key':None})

def _delegate_call(app,key,module,name):
    _handler = _getHandler(app,key)
    return _delegate(_handler,module,name)

# @_app.route('/api/<module>/<name>')
@_app.route("/<app_id>/<key>/api/<module>/<name>", methods=['GET'])
@_app.route("/api/<module>/<name>",defaults={'app_id':'main','key':None})
@_app.route("/<app_id>/api/<module>/<name>",defaults={'key':None})

def _api(app_id,key,module,name) :
    """
    This endpoint will load a module and make a function call
    :_module entry specified in plugins of the configuration
    :_name  name of the function to execute
    """
    
    _handler = _getHandler( app_id,key)
    return _delegate(_handler,module,name)

def _delegate(_handler,module,name):
    global _route
    uri =  '/'.join(['api',module,name])
    # _args = dict(request.args,**{})
    # _args['config'] = _handler.config()
    _plugins = _handler.plugins()
    _context = _handler.system()['context']
    if _context :
        uri  = f'{_context}/{uri}'
    _mimeType = 'application/octet-stream'
    if uri not in _plugins :
        _data = {}
        _code = 404
    else:
        pointer = _plugins[uri]
        # if _args :
        #     _data = pointer (**_args)
        # else:
        #     _data = pointer()
        
        _data,_mimeType = pointer(request=request,config=_handler.config())
        
        _mimeType = 'application/octet-stream' if not _mimeType else _mimeType
        if type(_data) == pd.DataFrame :
            _data = _data.to_dict(orient='records')
        if type(_data) == list:
            _data = json.dumps(_data)   
        _code = 200 if _data else 500
    return _data,_code,{'Content-Type':_mimeType}

# @_app.route("/api/<module>/<name>" , methods=['POST'],defaults={'app_id':'main','key':None})
# @_app.route('/<app_id>/api/<module>/<name>',methods=['POST'],defaults={'key':None})
# @_app.route('/<app_id>/<key>/api/<module>/<name>',methods=['POST'],defaults={'app_id':'main','key':None})

@_app.route("/<app_id>/<key>/api/<module>/<name>", methods=['POST'])
@_app.route("/api/<module>/<name>",defaults={'app_id':'main','key':None},methods=['POST'])
@_app.route("/<app_id>/api/<module>/<name>",defaults={'key':None},methods=['POST'])

def _post (app_id,key,module,name):
    # global _config
    # global _route
    # _handler = _route.get()
    # app_id = '/'.join([app_id,key]) if key else app_id
    _handler = _getHandler(app_id,key)
    return _delegate(_handler,module,name)

@_app.route('/version')
def _version ():
    global _route
    _handler = _route.get()
    global _config 
    return _handler.system()['version']
@_app.route("/reload/<key>")
def _reload(key) :
    global _route

    _handler = _route.get_main()
    _system = _handler.system()
    if not 'source' in _system :
        _systemKey = None
    elif 'key' in _system['source'] and _system['source']['key']:
        _systemKey = _system['source']['key']
        print ([key,_systemKey,_systemKey == key])
        if key and _systemKey and _systemKey == key :
            _handler.reload()
            return "",200
        pass
    return "",403    

@_app.route('/reload',methods=['POST'])
def reload():
    # global _route

    # _handler = _route.get_main()
    # _system = _handler.system()
    _key = request.headers['key'] if 'key' in request.headers else None
    return _reload(_key)
    # if not 'source' in _system :
    #     _systemKey = None
    # elif 'key' in _system['source'] and _system['source']['key']:
    #     _systemKey = _system['source']['key']
    #     print ([_key,_systemKey,_systemKey == _key])
    #     if _key and _systemKey and _systemKey == _key :
    #         _handler.reload()
    #         return "",200
    #     pass
    # return "",403

@_app.route('/page',methods=['POST'],defaults={'app_id':'main','key':None})
@_app.route('/<app_id>/page',methods=['POST'],defaults={'key':None})
@_app.route('/<app_id>/<key>/page',methods=['POST'])
def _POST_CMSPage(app_id,key):
    """
    return the content of a folder formatted for a menu
    """
    # global _config
    global _route
    # _handler = _route.get()
    # _config = _handler.config()
    _handler = _getHandler(app_id,key)
    _setHandler(app_id,key)
    
    _config = _handler.config()
    # _uri = os.sep.join([_config['layout']['root'],request.headers['uri']])
    _uri = request.headers['uri']
    if 'dom' not in request.headers :
        _id = _uri.split('/')[-1].split('.')[0]
    else:
        _id = request.headers['dom']
    # _args = {'layout':_config['layout']}
    # if 'plugins' in _config:
    #     _args['routes'] = _config['plugins']
    
    # _system = _handler.system() #cms.components.get_system(_config)
    # # _html =  _handler.html(_uri,_id,_args,_system) #cms.components.html(_uri,_id,_args,_system)

    # _html =  _handler.html(_uri,_id)
    # # _system = cms.components.get_system(_config)
    # _args['system'] = _handler.system(skip=['source','app'])
    # e = Environment(loader=BaseLoader()).from_string(_html)
    # _html = e.render(**_args)
    if 'read?uri=' in _uri or 'download?doc=' in _uri :
        _uri = _uri.split('=')[1]
    
    _args = _route.render(_uri,_id,_getId(app_id,key)) #session.get(app_id,'main'))
    return _args[_id],200
    # return _html,200
@_app.route('/page',defaults={'app_id':'main','resource':None})
@_app.route('/<app_id>/page',defaults={'resource':None},methods=['GET'])
@_app.route('/<app_id>/<resource>/page',methods=['GET'])
def _cms_page (app_id,resource):
    # global _config
    global _route
    # _handler = _route.get()
    # _config = _handler.config()
    _uri = request.args['uri']
    
    # _uri = os.sep.join([_config['layout']['root'],_uri])
    _title = request.args['title'] if 'title' in request.args else ''
    _args = _route.render(_uri,_title,session.get(app_id,'main'))
    return _args[_title],200

# @_app.route('/set/<id>')
# def set(id):
#     global _route
#     _setHandler(id)
#     # _route.set(id)
#     # _handler = _route.get()
#     _handler = _getHandler()
#     _context = _handler.system()['context']
#     _uri = f'/{_context}'.replace('//','/')
#     return redirect(_uri)
# @_app.route('/<id>')    
# def _open(id):
#     global _route
#     # _handler = _route.get()

#     _handler = _getHandler()
#     if id not in _route._apps :

#         _args = {'config':_handler.config(), 'layout':_handler.layout(),'system':_handler.system(skip=['source','app'])}
#         return render_template("404.html",**_args)
#     else:
#         _setHandler(id)
#         # _route.set(id)
#         return _index()


@cli.command()
def start (
    path:Annotated[str,typer.Argument(help="path of the manifest file")]='qcms-manifest.json',
    port:int=typer.Option(default=None, help="port number to serve on (will override configuration)")

    ) :
    """
    This function is designed to start the application with its associated manifest (configuration) location
    :path   path to the  the manifest
    :shared run in shared mode i.e 
    """
    global _route

    if os.path.exists(path) and os.path.isfile(path):
        _args = {'path':path}
        # if shared :
        #     _args['location'] = path
        #     _args['shared'] = shared
        _args['location'] = path
        _args['shared'] = True
        
        # _route = cms.engine.Router(**_args) #path=path,shared=shared)
        
        _route = cms.engine.basic.CMS(**_args)
        
        # dir(_route)
        # _args = _route.get().get_app()
        _args = _route.get().app()
        if port :
            _args['port'] = port
        _app.secret_key = str(uuid.uuid4())
        _app.config['PERMANENT_SESSION_LIFETIME'] = datetime.timedelta(hours=24)
        _app.run(**_args)
        _status = 'found'
    else:
        _status = 'not found'
    print(f'''
            manifest: {path}
            status  : {_status}
        ''')
@cli.command(name='help')
def _help() :
    pass
if __name__ == '__main__' :
    cli()
