"""Vendor tools for Lubwise ERP.

  python manage.py modules
  python manage.py license --licensee "Acme Trading" --modules sales,inventory --users 10 --branches 3 \
      --expires 2027-12-31 [--out instance/license.json]
  python manage.py license --show
  python manage.py recurring [--date YYYY-MM-DD]     create recurring documents due (run daily, e.g. from cron)
  python manage.py backup                            database + uploaded files into instance/backups
  python manage.py daily                             recurring documents, approval reminders, rent invoices (after
                                                     month end), preventive maintenance orders and a backup
  python manage.py zatca                             send e-invoices waiting for ZATCA (run hourly)
  python manage.py prime <username> [--off] [--create --password X]
                                                     make a login the software provider's prime super admin
                                                     (hidden from the customer; modules, code editor, branches, backups)
  python manage.py code-undo [--list]                undo the last Code Editor change (works even if the app won't start)

Set LUBWISE_LICENSE_SECRET to your private secret when issuing licenses for customers, and set the same
value on the customer's server.
"""
import argparse
import json
import os
import sys

BASE = os.path.dirname(os.path.abspath(__file__))


def code_undo(argv):
    """Pure sqlite + file write, no Lubwise import: must work even when the application code is broken."""
    import sqlite3
    from datetime import datetime
    path = os.path.join(BASE, "instance", "lubwise.db")
    if not os.path.exists(path) and os.path.exists(os.path.join(BASE, "instance", "vertex.db")):
        path = os.path.join(BASE, "instance", "vertex.db")   # not yet started since the rename
    db = sqlite3.connect(path)
    db.row_factory = sqlite3.Row
    if "--list" in argv:
        for r in db.execute("SELECT id, path, at, note FROM code_changes ORDER BY id DESC LIMIT 20"):
            print(f"#{r['id']}  {r['at']}  {r['path']}  {r['note'] or ''}")
        return
    r = db.execute("SELECT * FROM code_changes ORDER BY id DESC LIMIT 1").fetchone()
    if not r:
        print("No Code Editor changes to undo.")
        return
    full = os.path.realpath(os.path.join(BASE, r["path"]))
    if not full.startswith(os.path.realpath(BASE) + os.sep):
        sys.exit("refusing: path outside the application")
    with open(full, encoding="utf-8") as f:
        current = f.read()
    with open(full, "w", encoding="utf-8", newline="\n") as f:
        f.write(r["before"])
    db.execute("INSERT INTO code_changes (path, at, user_id, before, after, note) VALUES (?,?,?,?,?,?)",
               (r["path"], datetime.now().isoformat(timespec="seconds"), None, current, r["before"],
                f"code-undo of change #{r['id']} (server)"))
    db.commit()
    print(f"Restored {r['path']} to before change #{r['id']}. Start the application again.")


if len(sys.argv) > 1 and sys.argv[1] == "code-undo":
    code_undo(sys.argv[2:])
    sys.exit(0)

from lubwise.modules import DEV_SECRET, MODULES, License, make_license

DEFAULT_PATH = os.path.join(BASE, "instance", "license.json")


def main():
    ap = argparse.ArgumentParser(description="Lubwise ERP vendor tools")
    sub = ap.add_subparsers(dest="cmd", required=True)
    sub.add_parser("modules", help="list available modules")
    lp = sub.add_parser("license", help="issue or show a license")
    lp.add_argument("--licensee")
    lp.add_argument("--modules", default="", help="comma separated, or 'all'")
    lp.add_argument("--users", type=int, default=0, help="0 = unlimited")
    lp.add_argument("--branches", type=int, default=0, help="0 = unlimited")
    lp.add_argument("--expires", help="YYYY-MM-DD")
    lp.add_argument("--out", default=DEFAULT_PATH)
    lp.add_argument("--show", action="store_true")
    rp = sub.add_parser("recurring", help="create the recurring invoices, bills and journal entries that are due")
    rp.add_argument("--date", help="create documents due up to this date (default: today)")
    sub.add_parser("backup", help="back up the database and uploaded files")
    sub.add_parser("daily", help="recurring documents, approval reminders and a backup (run once a day)")
    sub.add_parser("zatca", help="send e-invoices waiting for ZATCA (run hourly; B2C must be reported within 24 h)")
    for name in ("prime", "superadmin"):  # "superadmin" is the older name of the same command
        sp = sub.add_parser(name, help="make a login the software provider's prime super admin")
        sp.add_argument("username")
        sp.add_argument("--off", action="store_true")
        sp.add_argument("--create", action="store_true", help="create the login if it does not exist")
        sp.add_argument("--password", help="password for --create (asked when not given)")
    sub.add_parser("code-undo", help="undo the last Code Editor change")
    args = ap.parse_args()

    if args.cmd in ("prime", "superadmin"):
        from lubwise import create_app
        from lubwise.db import commit, q, x
        app = create_app(os.environ.get("LUBWISE_DB")) if os.environ.get("LUBWISE_DB") else create_app()
        with app.app_context():
            u = q("SELECT id FROM users WHERE username = ?", (args.username,), one=True)
            if not u and args.create:
                import getpass
                from werkzeug.security import generate_password_hash
                pwd = args.password or getpass.getpass("Password: ")
                if len(pwd) < 8:
                    sys.exit("use at least 8 characters")
                x("INSERT INTO users (username, password_hash, full_name, role, lang, all_branches, active) "
                  "VALUES (?,?,?,'admin','en',1,1)",
                  (args.username, generate_password_hash(pwd, method="pbkdf2:sha256"), "Software provider"))
                u = q("SELECT id FROM users WHERE username = ?", (args.username,), one=True)
            if not u:
                sys.exit(f"no user {args.username!r} (add --create to make it)")
            x("UPDATE users SET level = ?, is_superadmin = ?, role = 'admin', all_branches = 1 WHERE id = ?",
              (None if args.off else "prime", 0 if args.off else 1, u["id"]))
            commit()
        print(f"{args.username}: {'normal login' if args.off else 'prime super admin (software provider)'}")
        return

    if args.cmd in ("backup", "daily"):
        from datetime import date
        from lubwise import create_app
        from lubwise.db import commit
        app = create_app()
        with app.test_request_context():
            if args.cmd == "daily":
                from lubwise.recurring import run
                from lubwise.workflow import send_reminders
                made = run(date.today().isoformat(), None)
                commit()
                print(f"recurring: {sum(1 for r in made if not r['error'])} created, "
                      f"{sum(1 for r in made if r['error'])} errors")
                n = send_reminders()
                commit()
                print(f"approval reminders: {n if n is not None else 'sent'}")
                from lubwise.vendorportal import expiry_reminders
                print(f"vendor document reminders: {expiry_reminders()}")
                commit()
                from lubwise.portables import bill_due, generate_preventive
                rents = bill_due()
                commit()
                print(f"rent invoices: {len(rents)}")
                pm = generate_preventive()
                commit()
                print(f"preventive maintenance orders opened: {len(pm)}")
                from lubwise.zatca import submit_pending
                sent, left = submit_pending()
                commit()
                print(f"ZATCA e-invoices: {sent} sent, {left} waiting")
                from lubwise.partneraccts import sweep
                print(f"partner account transfers: {sweep()}")
                commit()
            from lubwise.backup import make_backup
            print("backup:", make_backup("daily" if args.cmd == "daily" else "manual"))
        return

    if args.cmd == "zatca":
        from lubwise import create_app
        from lubwise.db import commit
        from lubwise.zatca import submit_pending
        app = create_app()
        with app.test_request_context():
            sent, left = submit_pending(limit=1000)
            commit()
        print(f"ZATCA e-invoices: {sent} sent, {left} waiting")
        return

    if args.cmd == "recurring":
        from datetime import date
        from lubwise import create_app
        from lubwise.db import commit
        from lubwise.recurring import run
        app = create_app()
        with app.test_request_context():
            results = run(args.date or date.today().isoformat(), None)
            commit()
        for r in results:
            status = f"ERROR {r['error']}" if r["error"] else f"{r['model']} #{r['id']}" + (f" ({r['note']})" if r.get("note") else "")
            print(f"{r['date']}  {r['rec']['name']}: {status}")
        print(f"{sum(1 for r in results if not r['error'])} document(s) created.")
        return

    if args.cmd == "modules":
        for key, m in MODULES.items():
            print(f"{key:12} {m['name']:22} {m['desc']}")
        return
    if args.show:
        lic = License(args.out)
        if not lic.licensed:
            print("No license file: all modules enabled (development mode).")
        else:
            print(json.dumps(lic.data, indent=2, ensure_ascii=False))
            print("valid" if lic.valid else f"INVALID: {lic.error}", "· expired" if lic.expired else "")
        return
    if not args.licensee:
        sys.exit("--licensee is required")
    mods = list(MODULES) if args.modules == "all" else [m.strip() for m in args.modules.split(",") if m.strip()]
    lic = make_license(args.licensee, mods, args.users, args.branches, args.expires)
    os.makedirs(os.path.dirname(os.path.abspath(args.out)), exist_ok=True)
    with open(args.out, "w", encoding="utf-8") as f:
        json.dump(lic, f, indent=2, ensure_ascii=False)
    if os.environ.get("LUBWISE_LICENSE_SECRET", DEV_SECRET) == DEV_SECRET:
        print("warning: signed with the development secret; set LUBWISE_LICENSE_SECRET for real customers")
    print(f"License for {args.licensee}: {', '.join(lic['modules'])} -> {args.out}")


if __name__ == "__main__":
    main()
