# Copyright (c) 2025 Thomas Goirand <zigo@debian.org>
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#    http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import threading

from flask import request, jsonify
from functools import wraps

from oslo_policy import policy as oslo_policy
from oslo_context import context as oslo_context
from oslo_log import log as logging

# Import your policies explicitly
from vmms.policy import rules as vmms_rules

LOG = logging.getLogger(__name__)

_ENFORCER = None


def init_enforcer(CONF):
    global _ENFORCER
    if not _ENFORCER:
        #LOG.debug("🐛 Initializing policy enforcer")
        # Create enforcer with proper configuration
        _ENFORCER = oslo_policy.Enforcer(CONF, policy_file='', rules={})

        # Explicitly register your policies
        #LOG.debug("🐛 Registering VMMS policy rules")
        for rule in vmms_rules.list_rules():
            _ENFORCER.register_default(rule)

        # Load the rules after registering defaults
        _ENFORCER.load_rules(force_reload=True)

        #LOG.debug(f"🐛 Registered {len(_ENFORCER.registered_rules)} policy rules")
            
    return _ENFORCER


def get_enforcer(CONF):
    enforcer = _ENFORCER or init_enforcer(CONF)
    return enforcer


def _get_keystone_token_info():
    """Return the authenticated token information from Keystone middleware."""
    token_info = request.environ.get('keystone.token_info')

    if not token_info:
        LOG.warning("Keystone middleware did not provide token information")
        return None

    return token_info


def _get_request_context():
    """Build an oslo.context RequestContext from Keystone middleware data."""
    token_info = _get_keystone_token_info()

    if not token_info:
        return None

    token = token_info.get('token', {})

    user = token.get('user', {})
    project = token.get('project', {})
    roles_data = token.get('roles', [])

    roles = [
        role.get('name')
        for role in roles_data
        if role.get('name')
    ]

    ctx = oslo_context.RequestContext(
        user_id=user.get('id'),
        project_id=project.get('id'),
        roles=roles,
        is_admin='admin' in roles,
    )

    return ctx


def enforce_policy(CONF, action, target=None):
    """Enforce policy for the current Flask request"""
    
    ctx = _get_request_context()

    if ctx is None:
        raise PermissionError('Authentication required')

    enforcer = get_enforcer(CONF)
    
    # Set default target if not provided
    if target is None:
        target = {
            'project_id': ctx.project_id or '',
            'user_id': ctx.user_id or ''
        }
    
    # Get credentials from context
    creds = ctx.to_policy_values()

    try:
        result = enforcer.enforce(action, target, creds)
        
        #LOG.debug(
        #    "Policy check: action=%s user=%s project=%s roles=%s "
        #    "target=%s result=%s",
        #    action,
        #    ctx.user_id,
        #    ctx.project_id,
        #    ctx.roles,
        #    target,
        #    result,
        #)

        if not result:
            raise oslo_policy.PolicyNotAuthorized(action, target, creds)

        return result

    except oslo_policy.PolicyNotAuthorized:
        raise

    except Exception:
        LOG.exception(
            "Policy enforcement failed for action %s",
            action,
        )
        raise

def require_policy_factory(get_config_func):
    """Factory function to create policy decorator with config getter"""
    def require_policy(action):
        """Decorator to enforce policy on API endpoints"""
        def decorator(f):
            @wraps(f)
            def wrapper(*args, **kwargs):
                # Authentication is performed by keystonemiddleware.
                # Do not trust X-Identity-Status from the HTTP request itself.
                if not _get_keystone_token_info():
                    return jsonify({'error': 'Authentication required'}), 401
                
                try:
                    CONF = get_config_func()
                    enforce_policy(CONF, action)

                except oslo_policy.PolicyNotAuthorized:
                    LOG.warning(
                        "Policy denied action %s for request",
                        action,
                    )
                    return jsonify({'error': 'Forbidden'}), 403

                except PermissionError:
                    return jsonify({'error': 'Authentication required'}), 401

                except KeyError as e:
                    LOG.error(f"⧱ Policy key error: {e}")
                    return jsonify({'error': 'Policy configuration error'}), 500

                except oslo_policy.PolicyNotAuthorized:
                    LOG.warning(
                        "Policy denied action %s for request",
                        action,
                    )
                    return jsonify({'error': 'Forbidden'}), 403

                except Exception:
                    LOG.exception(
                        "Unexpected error enforcing policy %s",
                        action,
                    )
                    return jsonify(
                        {'error': 'Policy enforcement error'}
                    ), 500
                    
                return f(*args, **kwargs)
            return wrapper
        return decorator
    return require_policy
