"""Security helpers; secrets and limits are shared across Passenger processes."""
import hashlib
import hmac
import os
import secrets
import time
from pathlib import Path


def secret_key(root):
    configured = os.getenv('RH_SECRET_KEY', '')
    if configured:
        if len(configured) < 32:
            raise RuntimeError('RH_SECRET_KEY must contain at least 32 characters')
        return configured
    if os.getenv('RH_ENV') == 'production':
        raise RuntimeError('RH_SECRET_KEY is required in production')
    folder = root / 'instance'
    folder.mkdir(exist_ok=True)
    path = folder / '.session-key'
    try:
        with path.open('x', encoding='ascii') as output:
            os.chmod(path, 0o600)
            output.write(secrets.token_hex(32))
    except FileExistsError:
        pass
    value = path.read_text(encoding='ascii').strip()
    if len(value) < 32:
        raise RuntimeError('Invalid local session key; restart after checking instance/.session-key')
    return value


def initialize_security(conn):
    conn.execute('CREATE TABLE IF NOT EXISTS login_limits (bucket TEXT PRIMARY KEY, attempts INTEGER NOT NULL, expires BIGINT NOT NULL)')
    conn.commit()


def login_attempt_allowed(conn, key, username, address):
    now = int(time.time())
    window = now // 900
    allowed = True
    for category, value, limit in (('account', username.casefold(), 20), ('ip', address, 100)):
        digest = hmac.new(key.encode(), value.encode(), hashlib.sha256).hexdigest()
        bucket = f'{category}:{window}:{digest}'
        row = conn.execute('''INSERT INTO login_limits(bucket,attempts,expires) VALUES(?,1,?)
            ON CONFLICT(bucket) DO UPDATE SET attempts=login_limits.attempts+1 RETURNING attempts''',
            (bucket, (window+1)*900)).fetchone()
        allowed = allowed and row['attempts'] <= limit
    conn.execute('DELETE FROM login_limits WHERE expires<?', (now,))
    conn.commit()
    return allowed
