import sys
from pathlib import Path

from docopt import docopt

import cista
from cista import app, config, droppy, serve, server80
from cista.util import pwgen

del app, server80.app  # Only import needed, for Sanic multiprocessing

doc = f"""Cista {cista.__version__} - A file storage for the web.

Usage:
  cista [-c <confdir>] [-l <host>] [--import-droppy] [--dev] [<path>]
  cista [-c <confdir>] --user <name> [--privileged] [--password]

Options:
  -c CONFDIR        Custom config directory
  -l LISTEN-ADDR    Listen on
                       :8000 (localhost port, plain http)
                       <addr>:3000 (bind another address, port)
                       /path/to/unix.sock (unix socket)
                       example.com (run on 80 and 443 with LetsEncrypt)
  --import-droppy   Import Droppy config from ~/.droppy/config
  --dev             Developer mode (reloads, friendlier crashes, more logs)

Listen address, path and imported options are preserved in config, and only
custom config dir and dev mode need to be specified on subsequent runs.

User management:
  --user NAME       Create or modify user
  --privileged      Give the user full admin rights
  --password        Reset password
"""


def main():
    # Dev mode doesn't catch exceptions
    if "--dev" in sys.argv:
        return _main()
    # Normal mode keeps it quiet
    try:
        return _main()
    except Exception as e:
        print("Error:", e)
        return 1


def _main():
    args = docopt(doc)
    if args["--user"]:
        return _user(args)
    listen = args["-l"]
    # Validate arguments first
    if args["<path>"]:
        path = Path(args["<path>"]).resolve()
        if not path.is_dir():
            raise ValueError(f"No such directory: {path}")
    else:
        path = None
    _confdir(args)
    exists = config.conffile.exists()
    import_droppy = args["--import-droppy"]
    necessary_opts = exists or import_droppy or path and listen
    if not necessary_opts:
        # Maybe run without arguments
        print(doc)
        print(
            "No config file found! Get started with:\n  cista -l :8000 /path/to/files, or\n  cista -l example.com --import-droppy  # Uses Droppy files\n",
        )
        return 1
    settings = {}
    if import_droppy:
        if exists:
            raise ValueError(
                f"Importing Droppy: First remove the existing configuration:\n  rm {config.conffile}",
            )
        settings = droppy.readconf()
    if path:
        settings["path"] = path
    if listen:
        settings["listen"] = listen
    operation = config.update_config(settings)
    print(f"Config {operation}: {config.conffile}")
    # Prepare to serve
    unix = None
    url, _ = serve.parse_listen(config.config.listen)
    if not config.config.path.is_dir():
        raise ValueError(f"No such directory: {config.config.path}")
    extra = f" ({unix})" if unix else ""
    dev = args["--dev"]
    if dev:
        extra += " (dev mode)"
    print(f"Serving {config.config.path} at {url}{extra}")
    # Run the server
    serve.run(dev=dev)
    return 0


def _confdir(args):
    if args["-c"]:
        # Custom config directory
        confdir = Path(args["-c"]).resolve()
        if confdir.exists() and not confdir.is_dir():
            if confdir.name != config.conffile.name:
                raise ValueError("Config path is not a directory")
            # Accidentally pointed to the db.toml, use parent
            confdir = confdir.parent
        config.conffile = confdir / config.conffile.name


def _user(args):
    _confdir(args)
    config.load_config()
    name = args["--user"]
    if not name or not name.isidentifier():
        raise ValueError("Invalid username")
    config.load_config()
    u = config.config.users.get(name)
    info = f"User {name}" if u else f"New user {name}"
    changes = {}
    oldadmin = u and u.privileged
    if args["--privileged"]:
        changes["privileged"] = True
        info += " (already admin)" if oldadmin else " (made admin)"
    else:
        info += " (admin)" if oldadmin else ""
    if args["--password"] or not u:
        changes["password"] = pw = pwgen.generate()
        info += f"\n  Password: {pw}"
    res = config.update_user(args["--user"], changes)
    print(info)
    if res == "read":
        print("  No changes")


if __name__ == "__main__":
    sys.exit(main())
