import glob
import logging
import os
import pathlib
import sys

import flask
from flask_cors import CORS
import lxml.etree as xmltree

log = logging.getLogger()
pipe_stderr = logging.StreamHandler(sys.stderr)
pipe_stderr.setLevel(logging.DEBUG)
log.addHandler(pipe_stderr)

if getattr(sys, "frozen", False):
    path_appdir = pathlib.Path(sys._MEIPASS)
else:
    path_appdir = pathlib.Path.cwd()
log.critical(path_appdir)

## \internal
#  \brief Signals whether we are a desktop application (as opposed to a Docker container).
desktop_mode = False

from . import gamedata

## \internal
#  \brief The Flask instance. See <a href="https://flask.palletsprojects.com/en/stable/api/">Flask documentation</a>.
app = flask.Flask("seagull-game", root_path=path_appdir, template_folder="templates", static_folder="static")
CORS(app)
app.config["SEND_FILE_MAX_AGE_DEFAULT"] = 0
orig_url_for = app.url_for

xml_namespaces = {
    "rule": "seagull:rules",
    "items": "seagull:rules/items",
    "upgrades": "seagull:rules/upgrades"
}

#REDIS_HOST="stub-implementation.example.net"
#REDIS_PORT=6379
#REDIS_USER="seagull"
#REDIS_PASS="i am not a real password"
#state_cache = redis.Redis(host=REDIS_HOST, port=REDIS_PORT, username=REDIS_USER, password=REDIS_PASS)

# ham5 standing by...

## \internal
#  \brief Returns CDN URLs for files not present within the container itself, or copies VFS data out so Flask etc can use it.
def url_for_override(endpoint, *posargs, _anchor=None, _method=None, _scheme=None, _external=None, self=app, **values):
    if endpoint == "static":
        if not gamedata.vfs.exists(f"static/{values["filename"]}"):
            log.warning("requested {0} from local file, but it doesn't exist in this container. Redirecting to CDN...\n".format(values["filename"]))
            return "https://cdn.otl-hga.net/seagull/" + values["filename"]
        else:
            gamedata.vfs.copy_out(f"static/{values["filename"]}", dest=path_appdir.as_posix())
            return orig_url_for(endpoint, *posargs, _anchor=_anchor, _method=_method, _scheme=_scheme, _external=_external, **values)
    else:
        print(endpoint)
        return orig_url_for(endpoint, *posargs, _anchor=_anchor, _method=_method, _scheme=_scheme, _external=_external, **values)

app.url_for = url_for_override

schemas = []
pth_common_rule_schema = path_appdir / "basepak/rules/schemas/rules.xsd"
doc_common_rule_schema = xmltree.parse(pth_common_rule_schema.as_posix())
common_rule_schema = xmltree.XMLSchema(doc_common_rule_schema)

## \brief Validates all XML files in gamedata.
#  \internal
#
#  This is typically run on program startup. Other modules will register validators
#  and this function will check against them all.
def validate_xml_files():
    for schema in schemas:
        for file in gamedata.vfs.vfs.glob(schema[1]):
            pth_xml = file.path[1:]
            fd_xml = gamedata.vfs.open(pth_xml)
            doc = xmltree.parse(fd_xml)

            if not schema[0].validate(doc):
                log.error(f"Bogus XML document: {pth_xml}")

## \internal
#  \brief Base Flask rendering context. Generated with render_base_context().
base_context = {}
base_context_live = False

## \brief Renders a dialog template and sends it to the client.
#  \param dialog The dialog to render.
#  \api{GET} /dialog/`<dialog>`
@app.route("/dialog/<dialog>")
def render_dialog(dialog):
    if gamedata.vfs.exists(f"templates/{dialog}.j2"):
        gamedata.vfs.copy_out(f"templates/{dialog}.j2", dest=path_appdir.as_posix())
        if gamedata.vfs.exists(f"static/js/dlg-{dialog}.js"):
            gamedata.vfs.copy_out(f"static/js/dlg-{dialog}.js", dest=path_appdir.as_posix())
        if gamedata.vfs.exists(f"static/js/dlg-{dialog}.mjs"):
            gamedata.vfs.copy_out(f"static/js/dlg-{dialog}.mjs")
        return flask.render_template(f"{dialog}.j2")
    else:
        return "", 404

## \brief Prepares the base rendering context for Flask to serve our content.
def render_base_context():
    global base_context
    global base_context_live

    base_context["svchost"] = flask.request.host

    domain_components = flask.request.host.split(".")
    base_domain = ".".join(domain_components[-2:])

    gamedata.vfs.copy_dir("static", dest=path_appdir.as_posix())

    # all this wind up for...
    if base_domain == "otl-hga.net": # production, use assets from S3
        base_context["styles"] = ["https://cdn.otl-hga.net/seagull/css/seagull.css"]
        base_context["scripts"] = ["https://cdn.otl-hga.net/seagull/js/seagull.js", "https://cdn.otl-hga.net/seagull/js/konami.js"]
        base_context["seagull_pic"] = "https://cdn.otl-hga.net/seagull/image/seagull.jpg"
    else:                            # dev, serve files from here
        #print(base_domain)
        base_context["styles"] = [app.url_for("static", filename="css/seagull.css")]
        base_context["scripts"] = [(app.url_for("static", filename="js/konami.js"), True), (app.url_for("static", filename="js/seagull.js"), True)]
        base_context["seagull_pic"] = app.url_for("static", filename="image/seagull.jpg")

    base_context_live = True

## \brief Returns OK. Useful for health checks.
#  \api{GET} /core/ping
@app.route("/core/ping")
def healthcheck_ping():
    return flask.Response("OK", content_type="text/plain")

## \brief Informs the game we're about to request a file from JavaScript.
#  \internal
#  \api{POST} /core/ready_file
#  \apidata Plaintext path to the intended file.
@app.route("/core/ready_file", methods=["POST"])
def ready_file():
    gamedata.vfs.copy_out(flask.request.data)
    return flask.Response("OK", content_type="text/plain")