"""
The parts of the login system that need nothing but standard Python (no Flask), so the
admin tool can edit the approved list on any computer. Shared by auth.py and manage_users.py.
"""
import os
import re
import sqlite3

BASE_DIR      = os.path.dirname(os.path.abspath(__file__))
AUTH_DIR      = os.environ.get("ORION_AUTH_DIR", BASE_DIR)
DB_PATH       = os.path.join(AUTH_DIR, "users.db")
APPROVED_PATH = os.path.join(AUTH_DIR, "approved_emails.txt")
SECRET_PATH   = os.path.join(AUTH_DIR, ".secret_key")
EVENT_LOG     = os.path.join(AUTH_DIR, "auth_events.log")

MIN_PASSWORD = 10
MAX_PASSWORD = 128
EMAIL_RE     = re.compile(r"^[^@\s]{1,64}@[^@\s]+\.[^@\s]+$")


def normalize_email(raw):
    e = (raw or "").strip().lower()
    return e if len(e) <= 254 and EMAIL_RE.match(e) else ""


_approved_cache = {"mtime": None, "emails": frozenset()}

def approved_emails():
    """Emails allowed to have an account. Re-read whenever the file changes.
    A missing file means nobody is approved (fails closed)."""
    try:
        mtime = os.path.getmtime(APPROVED_PATH)
    except OSError:
        return frozenset()
    if _approved_cache["mtime"] != mtime:
        emails = set()
        with open(APPROVED_PATH, encoding="utf-8") as f:
            for line in f:
                e = normalize_email(line.split("#", 1)[0])
                if e:
                    emails.add(e)
        _approved_cache.update(mtime=mtime, emails=frozenset(emails))
    return _approved_cache["emails"]


def connect():
    os.makedirs(AUTH_DIR, exist_ok=True)
    first = not os.path.exists(DB_PATH)
    db = sqlite3.connect(DB_PATH, timeout=10)
    db.row_factory = sqlite3.Row
    db.execute("""CREATE TABLE IF NOT EXISTS users(
        email TEXT PRIMARY KEY, pw_hash TEXT NOT NULL,
        created TEXT NOT NULL, last_login TEXT, session_epoch INTEGER NOT NULL DEFAULT 0)""")
    db.execute("CREATE TABLE IF NOT EXISTS attempts(key TEXT NOT NULL, ts REAL NOT NULL)")
    db.execute("CREATE INDEX IF NOT EXISTS attempts_key ON attempts(key, ts)")
    db.commit()
    if first:
        try: os.chmod(DB_PATH, 0o600)
        except OSError: pass
    return db


def password_problem(pw, email):
    if len(pw) < MIN_PASSWORD:
        return f"Password must be at least {MIN_PASSWORD} characters."
    if len(pw) > MAX_PASSWORD:
        return f"Password must be {MAX_PASSWORD} characters or fewer."
    if pw.lower() == email or pw.lower() == email.split("@")[0]:
        return "Password can't be the same as your email."
    return ""
