"""Lubwise ERP — run with:  python app.py [--reset] [--empty] [--port 8000]"""
import argparse
import os

from lubwise import create_app
from lubwise.db import create_schema

parser = argparse.ArgumentParser(description="Lubwise ERP")
parser.add_argument("--reset", action="store_true", help="delete the database and start over")
parser.add_argument("--empty", action="store_true", help="when creating the database, skip demo transactions")
parser.add_argument("--host", default="127.0.0.1")
parser.add_argument("--port", type=int, default=8000)
parser.add_argument("--dev", action="store_true", help="use Flask's development server instead of Waitress")
parser.add_argument("--threads", type=int, default=8)
parser.add_argument("--workers", type=int, default=0,
                    help="worker processes (Linux/macOS, needs gunicorn). Default: one per CPU core, at most 8; "
                         "1 = a single process with threads (Waitress, also on Windows)")
parser.add_argument("--db", help="database file (default instance/lubwise.db)")
parser.add_argument("--business", default="manufacturing",
                    choices=["trading", "contracting", "manufacturing", "services", "custom"],
                    help="when creating the database: the type of business (its modules and demo story). "
                         "Other than manufacturing needs LUBWISE_EDITION=all")
args = parser.parse_args()
from lubwise.modules import edition, offered_business_types  # noqa: E402
if args.business not in offered_business_types():
    parser.error(f"--business {args.business} is not part of the {edition()} edition (set LUBWISE_EDITION=all)")

app = create_app(args.db) if args.db else create_app()
db_path = app.config["DATABASE"]
if args.reset:
    for suffix in ("", "-wal", "-shm"):
        if os.path.exists(db_path + suffix):
            os.remove(db_path + suffix)
if not os.path.exists(db_path):
    from lubwise.seed import seed
    create_schema(db_path)
    with app.app_context():
        seed(demo=not args.empty, business=args.business)
    print("Database created" + ("" if args.empty else " with demo data") + ".")
    print("Sign in as admin: the demo passwords are in the README" if args.business == "manufacturing" else "Log in with admin / admin123")


def serve_workers(workers, threads):
    """Several worker processes, each with a few threads. Python runs one thread at a time per process, so a busy
    page (a report reading many rows) slows every other user of the same process; separate processes use every
    CPU core and keep the others fast."""
    from gunicorn.app.base import BaseApplication

    class Server(BaseApplication):
        def load_config(self):
            for k, v in {"bind": f"{args.host}:{args.port}", "workers": workers, "threads": threads,
                         "worker_class": "gthread", "timeout": 300, "graceful_timeout": 30, "keepalive": 5,
                         "accesslog": None, "post_fork": post_fork}.items():
                self.cfg.set(k, v)

        def load(self):
            return app

    Server().run()


def post_fork(_server, _worker):
    import threading
    from lubwise import db
    db._local = threading.local()   # never share the parent's database connections with a worker


if __name__ == "__main__":
    workers = args.workers or min(os.cpu_count() or 1, 8)
    if not args.dev and workers > 1 and os.name != "nt":
        try:
            import gunicorn  # noqa: F401
            print(f"Lubwise ERP on http://{args.host}:{args.port} ({workers} workers x {min(args.threads, 4)} threads)")
            serve_workers(workers, min(args.threads, 4))
            raise SystemExit
        except ImportError:
            print("gunicorn is not installed: running a single process (pip install gunicorn for more workers)")
    try:
        if args.dev:
            raise ImportError
        from waitress import serve  # production server (Windows, macOS, Linux)
        print(f"Lubwise ERP on http://{args.host}:{args.port}")
        serve(app, host=args.host, port=args.port, threads=args.threads)
    except ImportError:
        app.run(host=args.host, port=args.port, debug=False)
