from flask import Flask, request, jsonify
from flask_sqlalchemy import SQLAlchemy
from flask_jwt_extended import JWTManager, create_access_token, jwt_required, get_jwt_identity, get_jwt
from werkzeug.security import generate_password_hash, check_password_hash
from datetime import timedelta
import os

db = SQLAlchemy()

# User model
class User(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(80), unique=True, nullable=False)
    password_hash = db.Column(db.String(120), nullable=False)
    email = db.Column(db.String(120), unique=True, nullable=True)
    created_at = db.Column(db.DateTime, server_default=db.func.now())
    updated_at = db.Column(db.DateTime, server_default=db.func.now(), onupdate=db.func.now())

    notes = db.relationship('Note', backref='author', lazy=True)

    def __repr__(self):
        return '<User %r>' % self.username

# Note model
class Note(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
    title = db.Column(db.String(200), nullable=False)
    content = db.Column(db.Text, nullable=True)
    created_at = db.Column(db.DateTime, server_default=db.func.now())
    updated_at = db.Column(db.DateTime, server_default=db.func.now(), onupdate=db.func.now())

    def __repr__(self):
        return '<Note %r>' % self.title

# JWT Blacklist (moved outside create_app)
blacklist = set()

def create_app(test_config=None):
    app = Flask(__name__)

    if test_config:
        app.config.from_mapping(test_config)
    else:
        # Database configuration
        app.config['SQLALCHEMY_DATABASE_URI'] = os.environ.get('DATABASE_URL', 'postgresql://user:password@db:5432/notes_app_db')
        app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False

    db.init_app(app)

    # JWT configuration
    app.config['JWT_SECRET_KEY'] = os.environ.get('JWT_SECRET_KEY', 'super-secret-key')
    app.config['JWT_ACCESS_TOKEN_EXPIRES'] = timedelta(hours=1)
    jwt = JWTManager(app)

    @jwt.token_in_blocklist_loader
    def check_if_token_in_blacklist(jwt_header, jwt_payload):
        jti = jwt_payload["jti"]
        return jti in blacklist

    # Routes
    @app.route('/api/auth/register', methods=['POST'])
    def register():
        data = request.get_json()
        username = data.get('username')
        email = data.get('email')
        password = data.get('password')

        if not username or not password:
            return jsonify({"msg": "Missing username or password"}), 400

        if User.query.filter_by(username=username).first():
            return jsonify({"msg": "Username already exists"}), 409

        hashed_password = generate_password_hash(password)
        new_user = User(username=username, email=email, password_hash=hashed_password)
        db.session.add(new_user)
        db.session.commit()

        return jsonify({"msg": "User created successfully", "user_id": new_user.id}), 201

    @app.route('/api/auth/login', methods=['POST'])
    def login():
        data = request.get_json()
        username = data.get('username')
        password = data.get('password')

        if not username or not password:
            return jsonify({"msg": "Missing username or password"}), 400

        user = User.query.filter_by(username=username).first()
        if user and check_password_hash(user.password_hash, password):
            access_token = create_access_token(identity=user.id)
            return jsonify(access_token=access_token), 200
        else:
            return jsonify({"msg": "Bad username or password"}), 401

    @app.route('/api/auth/logout', methods=['POST'])
    @jwt_required()
    def logout():
        jti = get_jwt()["jti"]
        blacklist.add(jti)
        return jsonify({"msg": "Successfully logged out"}), 200

    @app.route('/api/notes', methods=['POST'])
    @jwt_required()
    def create_note():
        current_user_id = get_jwt_identity()
        data = request.get_json()
        title = data.get('title')
        content = data.get('content')

        if not title:
            return jsonify({"msg": "Missing note title"}), 400

        new_note = Note(user_id=current_user_id, title=title, content=content)
        db.session.add(new_note)
        db.session.commit()
        return jsonify({
            "id": new_note.id,
            "title": new_note.title,
            "content": new_note.content,
            "created_at": new_note.created_at,
            "updated_at": new_note.updated_at
        }), 201

    @app.route('/api/notes', methods=['GET'])
    @jwt_required()
    def get_notes():
        current_user_id = get_jwt_identity()
        notes = Note.query.filter_by(user_id=current_user_id).all()
        output = []
        for note in notes:
            output.append({
                "id": note.id,
                "title": note.title,
                "content": note.content,
                "created_at": note.created_at,
                "updated_at": note.updated_at
            })
        return jsonify(output), 200

    @app.route('/api/notes/<int:note_id>', methods=['GET'])
    @jwt_required()
    def get_note(note_id):
        current_user_id = get_jwt_identity()
        note = Note.query.filter_by(id=note_id, user_id=current_user_id).first()
        if not note:
            return jsonify({"msg": "Note not found or unauthorized"}), 404
        return jsonify({
            "id": note.id,
            "title": note.title,
            "content": note.content,
            "created_at": note.created_at,
            "updated_at": note.updated_at
        }), 200

    @app.route('/api/notes/<int:note_id>', methods=['PUT'])
    @jwt_required()
    def update_note(note_id):
        current_user_id = get_jwt_identity()
        note = Note.query.filter_by(id=note_id, user_id=current_user_id).first()
        if not note:
            return jsonify({"msg": "Note not found or unauthorized"}), 404

        data = request.get_json()
        note.title = data.get('title', note.title)
        note.content = data.get('content', note.content)
        db.session.commit()
        return jsonify({
            "id": note.id,
            "title": note.title,
            "content": note.content,
            "created_at": note.created_at,
            "updated_at": note.updated_at
        }), 200

    @app.route('/api/notes/<int:note_id>', methods=['DELETE'])
    @jwt_required()
    def delete_note(note_id):
        current_user_id = get_jwt_identity()
        note = Note.query.filter_by(id=note_id, user_id=current_user_id).first()
        if not note:
            return jsonify({"msg": "Note not found or unauthorized"}), 404

        db.session.delete(note)
        db.session.commit()
        return jsonify({"msg": "Note deleted successfully"}), 200

    return app

if __name__ == '__main__':
    app = create_app()
    with app.app_context():
        db.create_all() # Create database tables
    app.run(host='0.0.0.0', port=5000, debug=True)