<?php
/**
 * Permission Override Controller
 *
 * REST API endpoints for managing user-specific permission overrides.
 *
 * @package FleekDash\API
 * @since 2.3.0
 */

namespace FleekDash\API;

use FleekDash\Core\AuthMiddleware;
use FleekDash\Core\PermissionManager;
use FleekDash\Core\PermissionOverrideManager;
use FleekDash\Realtime\RealtimeEventOutboxService;

defined('ABSPATH') || exit;

class PermissionOverrideController extends \WP_REST_Controller {
    protected $namespace = 'fleekdash/v1';

    private const MESSAGE_OVERRIDE_NOT_FOUND = 'Override not found';

    /**
     * Register routes
     */
    public function register_routes(): void {
        register_rest_route($this->namespace, '/permissions/overrides', [
            'methods' => 'POST',
            'callback' => [$this, 'create'],
            'permission_callback' => [$this, 'check_permissions'],
            'args' => $this->getCreateArgs(),
        ]);

        register_rest_route($this->namespace, '/permissions/overrides', [
            'methods' => 'GET',
            'callback' => [$this, 'list'],
            'permission_callback' => [$this, 'check_permissions'],
            'args' => $this->getListArgs(),
        ]);

        register_rest_route($this->namespace, '/permissions/overrides/me', [
            'methods' => 'GET',
            'callback' => [$this, 'list_me'],
            'permission_callback' => [$this, 'check_permissions'],
        ]);

        // Specific route MUST be registered before generic (?P<id>\d+)
        // Otherwise /permissions/overrides/user/123 could be wrongly matched by generic pattern
        register_rest_route($this->namespace, '/permissions/overrides/user/(?P<user_id>\d+)', [
            'methods' => \WP_REST_Server::DELETABLE,
            'callback' => [$this, 'delete_all_for_user'],
            'permission_callback' => [$this, 'check_permissions'],
            'args' => ['user_id' => ['required' => true, 'type' => 'integer', 'validate_callback' => 'rest_validate_request_arg']],
        ]);

        $idArg = ['id' => ['required' => true, 'type' => 'integer', 'validate_callback' => 'rest_validate_request_arg']];
        register_rest_route($this->namespace, '/permissions/overrides/(?P<id>\d+)', [
            'methods' => \WP_REST_Server::READABLE,
            'callback' => [$this, 'get'],
            'permission_callback' => [$this, 'check_permissions'],
            'args' => $idArg,
        ]);

        register_rest_route($this->namespace, '/permissions/overrides/(?P<id>\d+)', [
            'methods' => \WP_REST_Server::EDITABLE,
            'callback' => [$this, 'update'],
            'permission_callback' => [$this, 'check_permissions'],
            'args' => array_merge($idArg, $this->getUpdateArgs()),
        ]);

        register_rest_route($this->namespace, '/permissions/overrides/(?P<id>\d+)', [
            'methods' => \WP_REST_Server::DELETABLE,
            'callback' => [$this, 'delete'],
            'permission_callback' => [$this, 'check_permissions'],
            'args' => $idArg,
        ]);
    }

    /**
     * Check permissions for the request
     *
     * @param \WP_REST_Request $request
     * @return bool
     */
    public function check_permissions(\WP_REST_Request $request): bool {
        $path = $request->get_route();

        // /me endpoint requires authenticated level
        if (strpos($path, '/me') !== false) {
            $auth_check = AuthMiddleware::check('authenticated');
            return $auth_check['authenticated'] && AuthMiddleware::validateRestNonceForMutation($request);
        }

        // All other override endpoints require admin level
        $auth_check = AuthMiddleware::check('admin');
        return $auth_check['authenticated'] && AuthMiddleware::validateRestNonceForMutation($request);
    }

    /**
     * Create override
     */
    public function create(\WP_REST_Request $request): \WP_REST_Response {
        $user_id = (int) $request->get_param('user_id');
        $permission_id = \sanitize_text_field($request->get_param('permission_id'));
        $override_type = $request->get_param('override_type');
        $expires_at = $request->get_param('expires_at');

        $validation_error = $this->validateCreateRequest($user_id, $permission_id, $override_type, $expires_at);
        if ($validation_error) {
            return $validation_error;
        }

        $data = [
            'user_id' => $user_id,
            'permission_id' => $permission_id,
            'override_type' => $override_type,
            'reason' => $request->get_param('reason'),
            'created_by' => \get_current_user_id(),
            'expires_at' => $expires_at,
        ];

        $override = PermissionOverrideManager::create($data);

        if (!$override) {
            return new \WP_REST_Response(['success' => false, 'message' => 'Failed to create override'], 500);
        }

        $response = [
            'success' => true,
            'override' => $this->formatOverride($override),
        ];
        RealtimeEventOutboxService::emitPermissionsUpdated(
            (int) $user_id,
            [(string) $permission_id],
            'user'
        );

        $permission_node = PermissionManager::get($permission_id);
        if (!empty($permission_node['required_feature'])) {
            $has_feature = $this->checkFeatureForUser($permission_node['required_feature']);
            if (!$has_feature) {
                $response['warning'] = sprintf(
                    'This override will only take effect when the site plan includes the feature: %s',
                    $permission_node['required_feature']
                );
            }
        }

        return new \WP_REST_Response($response, 201);
    }

    /**
     * Validate create request parameters
     */
    private function validateCreateRequest(int $user_id, string $permission_id, ?string $override_type, ?string $expires_at): ?\WP_REST_Response {
        $error = null;

        if (!\get_userdata($user_id)) {
            $error = ['message' => 'User not found', 'status' => 404];
        } elseif (!PermissionManager::has($permission_id)) {
            $error = ['message' => 'Permission not found', 'status' => 404];
        } elseif (!in_array($override_type, PermissionOverrideManager::VALID_TYPES, true)) {
            $error = ['message' => 'Invalid override type', 'status' => 400];
        } elseif (!$this->currentUserCanGrantPermission($permission_id)) {
            $error = ['message' => 'You cannot grant a permission you do not hold', 'status' => 403];
        } else {
            $is_temp = in_array($override_type, [PermissionOverrideManager::TYPE_TEMP_ALLOW, PermissionOverrideManager::TYPE_TEMP_DENY], true);
            if ($is_temp && empty($expires_at)) {
                $error = ['message' => 'Expiration date required for temporary overrides', 'status' => 400];
            }
        }

        return $error ? new \WP_REST_Response(['success' => false, 'message' => $error['message']], $error['status']) : null;
    }

    private function currentUserCanGrantPermission(string $permission_id): bool {
        $node = PermissionManager::get($permission_id);
        $required_capability = is_array($node) ? ($node['required_capability'] ?? '') : '';

        return $required_capability === '' || \current_user_can((string) $required_capability);
    }

    /**
     * List overrides with filters.
     * Ensures override table exists before querying; returns empty list if table missing.
     */
    public function list(\WP_REST_Request $request): \WP_REST_Response {
        if (!$this->overrideTableExists()) {
            return new \WP_REST_Response([
                'success' => true,
                'overrides' => [],
                'total' => 0,
                'page' => 1,
                'per_page' => (int) ($request->get_param('per_page') ?? 20),
            ]);
        }

        $filters = [
            'user_id' => $request->get_param('user_id'),
            'permission_id' => $request->get_param('permission_id'),
            'status' => $request->get_param('status') ?? 'active',
            'page' => $request->get_param('page') ?? 1,
            'per_page' => $request->get_param('per_page') ?? 20,
        ];

        $result = PermissionOverrideManager::list($filters);
        $rows = isset($result['overrides']) && is_array($result['overrides']) ? $result['overrides'] : [];
        $overrides = array_map([$this, 'formatOverride'], $rows);

        $response = new \WP_REST_Response([
            'success' => true,
            'overrides' => $overrides,
            'total' => (int) ($result['total'] ?? 0),
            'page' => (int) ($result['page'] ?? 1),
            'per_page' => (int) ($result['per_page'] ?? 20),
        ]);

        return \FleekDash\Services\ResponseService::withNoCacheHeaders($response);
    }

    /**
     * Check if the overrides table exists (avoids query on missing table).
     *
     * @return bool
     */
    private function overrideTableExists(): bool {
        global $wpdb;
        $table = $wpdb->prefix . 'fleekdash_user_permission_overrides';

        return $wpdb->get_var($wpdb->prepare('SHOW TABLES LIKE %s', $table)) === $table;
    }

    /**
     * List current user's overrides
     */
    public function list_me(\WP_REST_Request $request): \WP_REST_Response {
        $user_id = \get_current_user_id();
        $result = PermissionOverrideManager::list([
            'user_id' => $user_id,
            'status' => 'active',
            'per_page' => 100,
        ]);

        $overrides = array_map([$this, 'formatOverride'], $result['overrides']);

        $response = new \WP_REST_Response([
            'success' => true,
            'overrides' => $overrides,
        ]);

        return \FleekDash\Services\ResponseService::withNoCacheHeaders($response);
    }

    /**
     * Get single override
     */
    public function get(\WP_REST_Request $request): \WP_REST_Response {
        $id = (int) $request->get_param('id');
        $override = PermissionOverrideManager::get($id);

        if (!$override) {
            return new \WP_REST_Response(['success' => false, 'message' => self::MESSAGE_OVERRIDE_NOT_FOUND], 404);
        }

        $response = new \WP_REST_Response([
            'success' => true,
            'override' => $this->formatOverride($override),
        ]);

        return \FleekDash\Services\ResponseService::withNoCacheHeaders($response);
    }

    /**
     * Update override
     */
    public function update(\WP_REST_Request $request): \WP_REST_Response {
        $id = (int) $request->get_param('id');
        $existing = PermissionOverrideManager::get($id);

        if (!$existing) {
            return new \WP_REST_Response(['success' => false, 'message' => self::MESSAGE_OVERRIDE_NOT_FOUND], 404);
        }

        $data = [
            'override_type' => $request->get_param('override_type'),
            'reason' => $request->get_param('reason'),
            'expires_at' => $request->get_param('expires_at'),
            'is_active' => $request->get_param('is_active'),
        ];

        $data = array_filter($data, function ($v) {
            return $v !== null;
        });

        $override = PermissionOverrideManager::update($id, $data);

        if (!$override) {
            return new \WP_REST_Response(['success' => false, 'message' => 'Failed to update override'], 500);
        }
        RealtimeEventOutboxService::emitPermissionsUpdated(
            (int) ($existing['user_id'] ?? 0),
            [(string) ($existing['permission_id'] ?? '')],
            'user'
        );

        $response = new \WP_REST_Response([
            'success' => true,
            'override' => $this->formatOverride($override),
        ]);

        return \FleekDash\Services\ResponseService::withNoCacheHeaders($response);
    }

    /**
     * Delete override
     */
    public function delete(\WP_REST_Request $request): \WP_REST_Response {
        $id = (int) $request->get_param('id');
        $existing = PermissionOverrideManager::get($id);

        if (!$existing) {
            return new \WP_REST_Response([
                'success' => true,
                'already_deleted' => true,
            ], 200);
        }

        $deleted = PermissionOverrideManager::delete($id);
        if (!$deleted) {
            return new \WP_REST_Response([
                'success' => false,
                'message' => 'Failed to delete override. Check database logs.',
            ], 500);
        }

        $permission_id = (string) ($existing['permission_id'] ?? '');
        $user_id = (int) ($existing['user_id'] ?? 0);

        RealtimeEventOutboxService::emitPermissionsUpdated(
            $user_id,
            [$permission_id],
            'user'
        );

        return new \WP_REST_Response([
            'success' => true,
            'permission_id' => $permission_id,
            'user_id' => $user_id,
        ], 200);
    }

    /**
     * Delete all overrides for a user
     */
    public function delete_all_for_user(\WP_REST_Request $request): \WP_REST_Response {
        $user_id = (int) $request->get_param('user_id');
        $count = PermissionOverrideManager::deleteAllForUser($user_id);

        return new \WP_REST_Response([
            'success' => true,
            'deleted' => $count,
        ]);
    }

    /**
     * Format override for API response
     */
    private function formatOverride(array $override): array {
        $user = \get_userdata($override['user_id'] ?? 0);
        return [
            'id' => (int) ($override['id'] ?? 0),
            'user_id' => (int) ($override['user_id'] ?? 0),
            'user_login' => $user ? $user->user_login : null,
            'permission_id' => $override['permission_id'] ?? '',
            'override_type' => $override['override_type'] ?? '',
            'reason' => $override['reason'] ?? null,
            'created_by' => (int) ($override['created_by'] ?? 0),
            'created_at' => $override['created_at'] ?? null,
            'expires_at' => $override['expires_at'] ?? null,
            'is_active' => (bool) ($override['is_active'] ?? false),
        ];
    }

    /**
     * Check if user has feature (plan includes it)
     */
    private function checkFeatureForUser(string $feature_key): bool {
        return PermissionManager::hasFeature($feature_key);
    }

    private function getCreateArgs(): array {
        return [
            'user_id' => ['required' => true, 'type' => 'integer', 'validate_callback' => 'rest_validate_request_arg'],
            'permission_id' => ['required' => true, 'type' => 'string', 'validate_callback' => 'rest_validate_request_arg'],
            'override_type' => [
                'required' => true,
                'type' => 'string',
                'enum' => PermissionOverrideManager::VALID_TYPES,
                'validate_callback' => 'rest_validate_request_arg',
            ],
            'reason' => ['type' => 'string', 'validate_callback' => 'rest_validate_request_arg'],
            'expires_at' => ['type' => 'string', 'validate_callback' => 'rest_validate_request_arg'],
        ];
    }

    private function getListArgs(): array {
        return [
            'user_id' => ['type' => 'integer', 'validate_callback' => 'rest_validate_request_arg'],
            'permission_id' => ['type' => 'string', 'validate_callback' => 'rest_validate_request_arg'],
            'status' => ['type' => 'string', 'enum' => ['active', 'expired', 'all'], 'default' => 'active'],
            'page' => ['type' => 'integer', 'default' => 1, 'minimum' => 1],
            'per_page' => ['type' => 'integer', 'default' => 20, 'minimum' => 1, 'maximum' => 100],
        ];
    }

    private function getUpdateArgs(): array {
        return [
            'override_type' => ['type' => 'string', 'enum' => PermissionOverrideManager::VALID_TYPES],
            'reason' => ['type' => 'string'],
            'expires_at' => ['type' => 'string'],
            'is_active' => ['type' => 'boolean'],
        ];
    }
}
