#!/usr/bin/env python3
from datetime import UTC, datetime, timedelta
from flask import Flask, g, jsonify, request
from jwt import encode
from pathlib import Path
from secrets import token_urlsafe
from sqlite3 import IntegrityError, Row, connect
from re import search

app = Flask(__name__)
app.secret_key = token_urlsafe(32)

private_key_bytes = Path('/jwt.pem').read_bytes()
public_key_bytes = Path('/jwt.pub').read_bytes()

database = 'db/auth.db'

def get_db():
    db = getattr(g, '_database', None)
    if db is None:
        db = g._database = connect(database)
    db.row_factory = Row
    return db

@app.teardown_appcontext
def close_connection(_):
    db = getattr(g, '_database', None)
    if db is not None:
        db.close()

@app.route('/public-key')
def public_key():
    return public_key_bytes

@app.route('/login', methods=['POST'])
def login():
    content = request.get_json(silent=True)
    if content is None:
        return jsonify({'success': False, 'error': 'bad json'})

    username = content.get('username')
    password = content.get('password')
    if not username or not password:
        return jsonify({'success': False, 'error': 'bad request'})

    db = get_db()
    cursor = db.execute('SELECT username FROM users WHERE username = ? AND password = ?', (username, password))
    row = cursor.fetchone()
    if row is None:
        return jsonify({'success': False, 'error': 'bad username or password'})
    username = row['username']

    now = datetime.now(UTC)
    token = encode(
        {
            'username': username,
            'iat': now,
            'exp': now + timedelta(hours=1),
        },
        private_key_bytes,
        algorithm='RS256'
    )
    return jsonify({'success': True, 'token': token})

@app.route('/register', methods=['POST'])
def register():
    content = request.get_json(silent=True)
    if content is None:
        return jsonify({'success': False, 'error': 'bad json'})

    username = content.get('username')
    password = content.get('password')
    if not username or not password:
        return jsonify({'success': False, 'error': 'bad request'})
    if not search(r'^[a-zA-Z0-9_-]*$', username):
        return jsonify({'success': False, 'error': 'bad username'})

    db = get_db()
    try:
        db.execute('INSERT INTO users VALUES (?, ?)', (username, password))
        db.commit()
    except IntegrityError:
        return jsonify({'success': False, 'error': 'user already exists'})

    return jsonify({'success': True})

with app.app_context():
    db = get_db()
    db.execute('CREATE TABLE IF NOT EXISTS users (username TEXT NOT NULL UNIQUE, password TEXT NOT NULL)')
    db.commit()

if __name__ == '__main__':
    app.run()
