#!/usr/bin/env python

import argparse
import importlib.util
import json
import logging
import os
import threading
import time
import webbrowser

import requests

# Setup logging so we can see what's happening in the config file.
logging.basicConfig(level=logging.INFO)

# Parse command-line arguments.
config_parser = argparse.ArgumentParser(add_help=False, description="Dashboard for managing data and monitoring experiments.")
config_parser.add_argument("--config", default=os.path.expanduser("~/.samlab/config.py"), help="Configuration file. Default: %(default)s")
config_parser.add_argument("--coverage", action="store_true", help="Enable code coverage.")
config_parser.add_argument("--no-config", action="store_true", help="Do not use a config file for initialization.")
arguments, remaining_argv = config_parser.parse_known_args()

if arguments.coverage:
    import coverage
    cov = coverage.Coverage(auto_data=True, config_file=False, data_suffix=True, include="samlab*")
    cov.start()

if not arguments.no_config:
    spec = importlib.util.spec_from_file_location("config", arguments.config)
    config = importlib.util.module_from_spec(spec)
    spec.loader.exec_module(config)
else:
    config = {}

parser = argparse.ArgumentParser(parents=[config_parser])
parser.add_argument("--certificate", default=getattr(config, "certificate", None), help="TLS certificate.  Default: %(default)s")
parser.add_argument("--data-dir", default=getattr(config, "data_dir", os.getcwd()), help="Data directory. Default: %(default)s")
parser.add_argument("--debug", default=getattr(config, "debug", False), action="store_true", help="Enable server debugging.")
parser.add_argument("--host", default=getattr(config, "host", "127.0.0.1"), help="Host interface for incoming connections. Default: %(default)s")
parser.add_argument("--key", default=getattr(config, "key", None), help="TLS private key.  Default: %(default)s")
parser.add_argument("--no-browser", default=getattr(config, "no_browser", False), action="store_true", help="Disable automatically opening a web browser at startup.")
parser.add_argument("--port", type=int, default=getattr(config, "port", 4000), help="Host port for incoming connections. Default: %(default)s")
parser.add_argument("--server-description", default=getattr(config, "server_description", None), help="Server description. Default: %(default)s")
parser.add_argument("--server-name", default=getattr(config, "server_name", "samlab"), help="Server name. Default: %(default)s")
parser.add_argument("--session-timeout", type=float, default=getattr(config, "session_timeout", 15 * 60), help="Session timeout in seconds. Default: %(default)s")
arguments = parser.parse_args(remaining_argv)

# Setup logging.
logging.basicConfig(level=logging.DEBUG if arguments.debug else logging.INFO)
logging.getLogger("fsevents").setLevel(logging.INFO)
logging.getLogger("engineio").setLevel(logging.ERROR)
logging.getLogger("matplotlib").setLevel(logging.INFO)
logging.getLogger("watchdog").setLevel(logging.INFO)
log = logging.getLogger()

# Setup the web server.
from samlab.dashboard.server import application, socketio
import samlab.dashboard.acl
import samlab.dashboard.authentication
import samlab.dashboard.credentials

application.config["acl"] = getattr(config, "acl", samlab.dashboard.acl.ForbidDeveloper())
application.config["authentication"] = getattr(config, "authentication", samlab.dashboard.authentication.Null())
application.config["certificate"] = arguments.certificate
application.config["credentials"] = getattr(config, "credentials", samlab.dashboard.credentials.PermitAny())
application.config["debug"] = arguments.debug
application.config["host"] = arguments.host
application.config["key"] = arguments.key
application.config["layout"] = getattr(config, "layout", [])
application.config["no-browser"] = arguments.no_browser
application.config["port"] = arguments.port
application.config["server-description"] = arguments.server_description
application.config["server-name"] = arguments.server_name
application.config["session-timeout"] = arguments.session_timeout

for key, value in sorted(application.config.items()):
    if key not in ["SECRET_KEY"]:
        log.info("Configuration: %s: %s", key, value)

# Setup services (note: must happen *after* all configuration).
import samlab.dashboard.service.asynchronous
import samlab.dashboard.service.basic
import samlab.dashboard.service.backends
import samlab.dashboard.service.documents
import samlab.dashboard.service.favorites
import samlab.dashboard.service.images
import samlab.dashboard.service.layouts
import samlab.dashboard.service.notify
import samlab.dashboard.service.timeseries

# Setup default mappings.
import samlab.dashboard.backend.favorites
import samlab.dashboard.backend.layouts

backends = [
    samlab.dashboard.backend.favorites.JSONFile(storage=os.path.expanduser("~/.samlab/favorites.json")),
    samlab.dashboard.backend.layouts.JSONFile(storage=os.path.expanduser("~/.samlab/layouts.json")),
    ] + getattr(config, "backends", [])

for backend in backends:
    samlab.dashboard.service.register_backend(backend)

# Optionally open a web browser at startup.
def open_browser(config):
    scheme = "https" if config["certificate"] and config["key"] else "http"
    server_name = config["host"]
    server_port = config["port"]
    uri = f"{scheme}://{server_name}:{server_port}"

    log.info(f"Will open {uri} in default web browser.")

    while True:
        try:
            requests.get(uri + "/ready", proxies={"http": None})
            webbrowser.open(uri)
            return
        except Exception as e:
            log.debug(e)
            time.sleep(1.0)

if not application.config["no-browser"]:
    threading.Thread(target=open_browser, args=(application.config,), daemon=True).start()

# Start the server.
kwargs = {
    "allow_unsafe_werkzeug": True,
    "debug": application.config["debug"],
    "host": application.config["host"],
    "port": application.config["port"],
}

if application.config["certificate"] and application.config["key"]:
    kwargs["ssl_context"] = (application.config["certificate"], application.config["key"])

socketio.run(application, **kwargs)
