#!/usr/bin/env python3
"""
Manage who can use the Orion Permit Portal.

  python3 manage_users.py list                    show approved emails and who has signed up
  python3 manage_users.py approve  a@x.com b@y.com   let these people sign up
  python3 manage_users.py unapprove a@x.com       remove from the list (also signs them out)
  python3 manage_users.py set-password a@x.com    set a new password (signs out their other devices; run on the server)
  python3 manage_users.py delete a@x.com          delete their account

Run it on the machine where server.py runs, so it edits the server's real list. If ORION_AUTH_DIR is
set for the server, set the same value here:   ORION_AUTH_DIR=/path python3 manage_users.py list
(Run on your own computer it just edits a local copy of the list, which does nothing for the live site.)
"""
import getpass
import os
import sys

import auth_core as core      # standard Python only — no Flask needed for the list commands


def _approved_lines():
    try:
        with open(core.APPROVED_PATH, encoding="utf-8") as f:
            return f.read().splitlines()
    except OSError:
        return []

def _write_approved(lines):
    os.makedirs(core.AUTH_DIR, exist_ok=True)
    with open(core.APPROVED_PATH, "w", encoding="utf-8") as f:
        f.write("\n".join(lines).rstrip("\n") + "\n")

PLACEHOLDER_DOMAINS = {"yourcompany.com", "yourdomain.com", "company.com", "example.com", "example.org", "domain.com"}

def _email(arg):
    e = core.normalize_email(arg)
    if not e:
        sys.exit(f"'{arg}' doesn't look like an email address.")
    if e.split("@")[1] in PLACEHOLDER_DOMAINS:
        sys.exit(f"'{e}' looks like a placeholder from the instructions — use a real address. Nothing was changed.")
    return e

def _users():
    return {r["email"]: r for r in core.connect().execute("SELECT * FROM users ORDER BY email")}

def cmd_list():
    approved, users = sorted(core.approved_emails()), _users()
    print(f"\nApproved list ({core.APPROVED_PATH}):")
    if not approved:
        print("  (empty — nobody can sign up yet. Add people with:  python3 manage_users.py approve name@company.com)")
    for e in approved:
        u = users.get(e)
        print(f"  {e:<40} " + (f"signed up {u['created'][:10]}, last login {(u['last_login'] or 'never')[:10]}" if u else "not signed up yet"))
    stray = [e for e in users if e not in approved]
    if stray:
        print("\nAccounts that are NOT on the approved list (these people are blocked from signing in):")
        for e in stray:
            print(f"  {e}")
    print()

def cmd_approve(emails):
    lines = _approved_lines()
    have = {core.normalize_email(l.split("#", 1)[0]) for l in lines}
    for raw in emails:
        e = _email(raw)
        if e in have:
            print(f"already approved: {e}")
        else:
            lines.append(e); have.add(e); print(f"approved: {e}")
    _write_approved(lines)
    print(f"\nList saved to: {core.APPROVED_PATH}")

def cmd_unapprove(emails):
    wanted = {_email(e) for e in emails}
    kept, removed = [], set()
    for line in _approved_lines():
        e = core.normalize_email(line.split("#", 1)[0])
        if e in wanted:
            removed.add(e)
        else:
            kept.append(line)
    _write_approved(kept)
    for e in wanted:
        print(("removed from list (and signed out): " if e in removed else "wasn't on the list: ") + e)

def cmd_set_password(raw):
    e = _email(raw)
    d = core.connect()
    if not d.execute("SELECT 1 FROM users WHERE email=?", (e,)).fetchone():
        sys.exit(f"{e} hasn't signed up yet. Approve them and have them sign up instead.")
    pw = getpass.getpass("New password: ")
    if pw != getpass.getpass("Again: "):
        sys.exit("Passwords didn't match — nothing changed.")
    problem = core.password_problem(pw, e)
    if problem:
        sys.exit(problem)
    try:
        from werkzeug.security import generate_password_hash
    except ImportError:
        sys.exit("Setting a password needs Flask/Werkzeug, which is installed on the server — run this there.")
    d.execute("UPDATE users SET pw_hash=?, session_epoch=session_epoch+1 WHERE email=?", (generate_password_hash(pw), e))
    d.execute("DELETE FROM attempts WHERE key=?", (f"login-email:{e}",))
    d.commit()
    print(f"Password updated for {e}. Any devices they were signed in on are now signed out.")

def cmd_delete(raw):
    e = _email(raw)
    d = core.connect()
    n = d.execute("DELETE FROM users WHERE email=?", (e,)).rowcount
    d.commit()
    print(f"deleted account for {e}" if n else f"no account for {e}")
    if e in core.approved_emails():
        print("Note: they're still on the approved list, so they could sign up again. "
              f"Run:  python3 manage_users.py unapprove {e}")

if __name__ == "__main__":
    a = sys.argv[1:]
    if not a or a[0] in ("-h", "--help", "help"):
        print(__doc__)
    elif a[0] == "list":
        cmd_list()
    elif a[0] == "approve" and len(a) > 1:
        cmd_approve(a[1:])
    elif a[0] == "unapprove" and len(a) > 1:
        cmd_unapprove(a[1:])
    elif a[0] == "set-password" and len(a) == 2:
        cmd_set_password(a[1])
    elif a[0] == "delete" and len(a) == 2:
        cmd_delete(a[1])
    else:
        sys.exit(__doc__)
