#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
RBAC Integration Example for Flask

This example shows how to integrate the RBAC system into a real Flask application
with realistic endpoints and permission requirements.
"""

from flask import Flask, jsonify, request
from Identitas.interface.flask.utils.auth_utils import require_session, require_right, require_resource_action
from Identitas.provider.api.init import init
from Identitas.provider.api.auth import register, login, logout
from Identitas.provider.api.rbac import (
    # Role management
    create_role, delete_role, get_role, get_role_by_name, list_roles, update_role,
    # Right management
    create_right, delete_right, get_right, get_right_by_name, list_rights, update_right,
    # User-Role assignment
    assign_role_to_user, remove_role_from_user, get_user_roles, get_users_with_role,
    # Role-Right assignment
    assign_right_to_role, remove_right_from_role, get_role_rights,
    # Permission checking
    user_has_right, user_has_resource_action, get_user_all_rights, get_user_permissions_summary
)
import os

# ============================================================
# Flask App Setup
# ============================================================

app = Flask(__name__)
os.environ["IDENTITAS_JWT"] = "true"
os.environ["IDENTITAS_SIGNUP_REQUIREMENTS"] = "min_length=12,upper=1,lower=1,digits=2,special=1"

debug_mode = os.getenv("DEBUG", "false").lower() == "true"
engine = init(os.path.join(os.path.dirname(__file__), "data/auth.db"), debug=debug_mode)

app.config["IDENTITAS_ENGINE"] = engine
app.config["IDENTITAS_LANG"] = "en"

# ============================================================
# INITIALIZATION: Create default roles and rights
# ============================================================

def initialize_rbac():
    """Initialize RBAC with default roles and rights"""
    
    # Create default roles
    admin_role = get_role_by_name(engine, "Administrator") or create_role(
        engine, "Administrator", "Full system access"
    )
    editor_role = get_role_by_name(engine, "Editor") or create_role(
        engine, "Editor", "Can create and edit content"
    )
    viewer_role = get_role_by_name(engine, "Viewer") or create_role(
        engine, "Viewer", "Can view content"
    )
    
    # Create default rights
    rights_config = [
        # User Management
        ("user:create", "user", "create", "Create new users"),
        ("user:read", "user", "read", "View user information"),
        ("user:update", "user", "update", "Modify user information"),
        ("user:delete", "user", "delete", "Delete users"),
        ("user:list", "user", "list", "List all users"),
        
        # Content Management
        ("content:create", "content", "create", "Create new content"),
        ("content:read", "content", "read", "View content"),
        ("content:update", "content", "update", "Modify content"),
        ("content:delete", "content", "delete", "Delete content"),
        ("content:publish", "content", "publish", "Publish content"),
        ("content:list", "content", "list", "List content"),
        
        # System
        ("system:settings", "system", "settings", "Manage system settings"),
        ("system:rbac", "system", "rbac", "Manage roles and rights"),
    ]
    
    for name, resource, action, description in rights_config:
        if not get_right_by_name(engine, name):
            create_right(engine, name, resource, action, description)
    
    # Assign all rights to Administrator
    all_rights = list_rights(engine)
    for right in all_rights:
        if right not in get_role_rights(engine, admin_role.id):
            assign_right_to_role(engine, admin_role.id, right.id)
    
    # Assign limited rights to Editor
    editor_rights = [
        "content:create", "content:read", "content:update", "content:delete",
        "content:list", "content:publish", "user:read", "user:list"
    ]
    for right_name in editor_rights:
        right = get_right_by_name(engine, right_name)
        if right and right not in get_role_rights(engine, editor_role.id):
            assign_right_to_role(engine, editor_role.id, right.id)
    
    # Assign read-only rights to Viewer
    viewer_rights = ["content:read", "content:list", "user:read", "user:list"]
    for right_name in viewer_rights:
        right = get_right_by_name(engine, right_name)
        if right and right not in get_role_rights(engine, viewer_role.id):
            assign_right_to_role(engine, viewer_role.id, right.id)


# Initialize RBAC on app start
initialize_rbac()

# ============================================================
# Authentication Endpoints
# ============================================================

@app.route("/auth/register", methods=["POST"])
def register_route():
    """Register a new user"""
    try:
        data = request.get_json()
        username = data.get("username")
        password = data.get("password")
        email = data.get("email")
        
        if not username or not password:
            return jsonify({"error": "username and password required"}), 400
        
        token = register(engine, username, password, email)
        if not token:
            return jsonify({"error": "Registration failed"}), 400
        
        # Assign default viewer role to new user
        from Identitas.provider.models.user import User
        from sqlalchemy.orm import Session as DBSession
        
        with DBSession(engine) as session:
            user = session.query(User).filter_by(username=username).first()
            if user:
                viewer_role = get_role_by_name(engine, "Viewer")
                if viewer_role:
                    assign_role_to_user(engine, user.id, viewer_role.id)
        
        return jsonify({
            "message": "User registered successfully",
            "token": token
        }), 201
    
    except Exception as e:
        return jsonify({"error": str(e)}), 500


@app.route("/auth/login", methods=["POST"])
def login_route():
    """Login user"""
    try:
        data = request.get_json()
        username = data.get("username")
        password = data.get("password")
        
        if not username or not password:
            return jsonify({"error": "username and password required"}), 400
        
        from Identitas.provider.api.auth import login
        token = login(engine, username, password)
        
        if not token:
            return jsonify({"error": "Invalid credentials"}), 401
        
        return jsonify({
            "message": "Login successful",
            "token": token
        }), 200
    
    except Exception as e:
        return jsonify({"error": str(e)}), 500


@app.route("/auth/logout", methods=["POST"])
@require_session
def logout_route(user_id):
    """Logout user"""
    try:
        token = request.cookies.get("identitas_session")
        if logout(engine, token):
            return jsonify({"message": "Logout successful"}), 200
        return jsonify({"error": "Logout failed"}), 400
    except Exception as e:
        return jsonify({"error": str(e)}), 500


@app.route("/auth/me", methods=["GET"])
@require_session
def get_current_user(user_id):
    """Get current user info and permissions"""
    try:
        from Identitas.provider.models.user import User
        from sqlalchemy.orm import Session as DBSession
        
        with DBSession(engine) as session:
            user = session.query(User).filter_by(id=user_id).first()
            if not user:
                return jsonify({"error": "User not found"}), 404
            
            roles = [role.name for role in get_user_roles(engine, user_id)]
            permissions = get_user_permissions_summary(engine, user_id)
            
            return jsonify({
                "id": user.id,
                "username": user.username,
                "email": user.email,
                "roles": roles,
                "permissions": permissions
            }), 200
    except Exception as e:
        return jsonify({"error": str(e)}), 500


# ============================================================
# Content Endpoints (with permission checks)
# ============================================================

# Mock content storage
content_store = {}
content_counter = 0


@app.route("/content", methods=["GET"])
@require_session
def list_content(user_id):
    """List all content - requires content:list"""
    try:
        if not user_has_resource_action(engine, user_id, "content", "list"):
            return jsonify({"error": "You don't have permission to list content"}), 403
        
        return jsonify({
            "content": list(content_store.values())
        }), 200
    except Exception as e:
        return jsonify({"error": str(e)}), 500


@app.route("/content", methods=["POST"])
@require_resource_action("content", "create")
def create_content(user_id):
    """Create new content - requires content:create"""
    try:
        global content_counter
        data = request.get_json()
        
        content_counter += 1
        content = {
            "id": content_counter,
            "title": data.get("title"),
            "body": data.get("body"),
            "author_id": user_id,
            "published": False
        }
        
        content_store[content_counter] = content
        
        return jsonify({
            "message": "Content created",
            "content": content
        }), 201
    except Exception as e:
        return jsonify({"error": str(e)}), 500


@app.route("/content/<int:content_id>", methods=["GET"])
@require_session
def get_content(user_id, content_id):
    """Get specific content - requires content:read"""
    try:
        if not user_has_resource_action(engine, user_id, "content", "read"):
            return jsonify({"error": "You don't have permission to read content"}), 403
        
        if content_id not in content_store:
            return jsonify({"error": "Content not found"}), 404
        
        return jsonify(content_store[content_id]), 200
    except Exception as e:
        return jsonify({"error": str(e)}), 500


@app.route("/content/<int:content_id>", methods=["PUT"])
@require_resource_action("content", "update")
def update_content(user_id, content_id):
    """Update content - requires content:update"""
    try:
        if content_id not in content_store:
            return jsonify({"error": "Content not found"}), 404
        
        data = request.get_json()
        content_store[content_id].update({
            "title": data.get("title", content_store[content_id]["title"]),
            "body": data.get("body", content_store[content_id]["body"])
        })
        
        return jsonify({
            "message": "Content updated",
            "content": content_store[content_id]
        }), 200
    except Exception as e:
        return jsonify({"error": str(e)}), 500


@app.route("/content/<int:content_id>/publish", methods=["POST"])
@require_resource_action("content", "publish")
def publish_content(user_id, content_id):
    """Publish content - requires content:publish"""
    try:
        if content_id not in content_store:
            return jsonify({"error": "Content not found"}), 404
        
        content_store[content_id]["published"] = True
        
        return jsonify({
            "message": "Content published",
            "content": content_store[content_id]
        }), 200
    except Exception as e:
        return jsonify({"error": str(e)}), 500


@app.route("/content/<int:content_id>", methods=["DELETE"])
@require_resource_action("content", "delete")
def delete_content(user_id, content_id):
    """Delete content - requires content:delete"""
    try:
        if content_id not in content_store:
            return jsonify({"error": "Content not found"}), 404
        
        del content_store[content_id]
        
        return jsonify({"message": "Content deleted"}), 200
    except Exception as e:
        return jsonify({"error": str(e)}), 500


# ============================================================
# User Management Endpoints (requires system:rbac)
# ============================================================

@app.route("/admin/users", methods=["GET"])
@require_right("user:list")
def list_users(user_id):
    """List all users - requires user:list"""
    try:
        from Identitas.provider.models.user import User
        from sqlalchemy.orm import Session as DBSession
        
        with DBSession(engine) as session:
            users = session.query(User).all()
            return jsonify({
                "users": [
                    {
                        "id": u.id,
                        "username": u.username,
                        "email": u.email,
                        "roles": [r.name for r in get_user_roles(engine, u.id)]
                    }
                    for u in users
                ]
            }), 200
    except Exception as e:
        return jsonify({"error": str(e)}), 500


@app.route("/admin/users/<int:user_id>/roles", methods=["GET"])
@require_right("user:read")
def get_user_roles_endpoint(user_id, current_user_id):
    """Get user's roles - requires user:read"""
    try:
        roles = get_user_roles(engine, user_id)
        return jsonify({
            "user_id": user_id,
            "roles": [
                {
                    "id": r.id,
                    "name": r.name,
                    "description": r.description
                }
                for r in roles
            ]
        }), 200
    except Exception as e:
        return jsonify({"error": str(e)}), 500


@app.route("/admin/users/<int:user_id>/roles/<int:role_id>", methods=["POST"])
@require_right("system:rbac")
def assign_role_endpoint(user_id, role_id, current_user_id):
    """Assign role to user - requires system:rbac"""
    try:
        if assign_role_to_user(engine, user_id, role_id):
            return jsonify({"message": "Role assigned"}), 200
        return jsonify({"error": "Failed to assign role"}), 400
    except Exception as e:
        return jsonify({"error": str(e)}), 500


@app.route("/admin/users/<int:user_id>/roles/<int:role_id>", methods=["DELETE"])
@require_right("system:rbac")
def remove_role_endpoint(user_id, role_id, current_user_id):
    """Remove role from user - requires system:rbac"""
    try:
        if remove_role_from_user(engine, user_id, role_id):
            return jsonify({"message": "Role removed"}), 200
        return jsonify({"error": "Failed to remove role"}), 400
    except Exception as e:
        return jsonify({"error": str(e)}), 500


# ============================================================
# Role Management Endpoints
# ============================================================

@app.route("/admin/roles", methods=["GET"])
@require_right("system:rbac")
def list_roles_endpoint(user_id):
    """List all roles - requires system:rbac"""
    try:
        roles = list_roles(engine)
        return jsonify({
            "roles": [
                {
                    "id": r.id,
                    "name": r.name,
                    "description": r.description,
                    "rights_count": len(get_role_rights(engine, r.id))
                }
                for r in roles
            ]
        }), 200
    except Exception as e:
        return jsonify({"error": str(e)}), 500


@app.route("/admin/roles", methods=["POST"])
@require_right("system:rbac")
def create_role_endpoint(user_id):
    """Create role - requires system:rbac"""
    try:
        data = request.get_json()
        role = create_role(engine, data.get("name"), data.get("description"))
        
        if not role:
            return jsonify({"error": "Failed to create role"}), 400
        
        return jsonify({
            "message": "Role created",
            "role": {
                "id": role.id,
                "name": role.name,
                "description": role.description
            }
        }), 201
    except Exception as e:
        return jsonify({"error": str(e)}), 500


# ============================================================
# Error Handlers
# ============================================================

@app.errorhandler(404)
def not_found(error):
    return jsonify({"error": "Not found"}), 404


@app.errorhandler(500)
def server_error(error):
    return jsonify({"error": "Server error"}), 500


# ============================================================
# Run Application
# ============================================================

if __name__ == "__main__":
    print("""
    ============================================================
    Identitas RBAC Flask Application
    ============================================================
    
    API Endpoints:
    
    Authentication:
      POST   /auth/register          - Register new user
      POST   /auth/login             - Login
      POST   /auth/logout            - Logout
      GET    /auth/me                - Get current user
    
    Content Management:
      GET    /content                - List content
      POST   /content                - Create content
      GET    /content/<id>           - Get content
      PUT    /content/<id>           - Update content
      POST   /content/<id>/publish   - Publish content
      DELETE /content/<id>           - Delete content
    
    User Management (Admin):
      GET    /admin/users            - List users
      GET    /admin/users/<id>/roles - Get user roles
      POST   /admin/users/<id>/roles/<rid> - Assign role
      DELETE /admin/users/<id>/roles/<rid> - Remove role
    
    Role Management (Admin):
      GET    /admin/roles            - List roles
      POST   /admin/roles            - Create role
    
    Default roles:
      - Administrator (all permissions)
      - Editor (content management + read user)
      - Viewer (read-only access)
    
    ============================================================
    """)
    app.run(debug=True)
