<?php
if (!defined('ABSPATH')) {
    exit;
}

/**
 * Service for handling user roles, capabilities, and platform permissions.
 */
class NEXDIRECTORY_User_Capabilities {

    /**
     * @var string The main post type slug for the platform.
     */
    private $post_type;

    /**
     * Constructor.
     * * @param string $post_type The primary post type used by the platform.
     */
    public function __construct($post_type = 'nexdirectory_event') {
        $this->post_type = sanitize_key($post_type);
    }

    /**
     * Resolves a user ID, falling back to the current logged-in user if 0 or omitted.
     */
    public function resolve_capability_user_id($user_id = 0) {
        $user_id = absint((int) $user_id);
        if ($user_id > 0) {
            return $user_id;
        }
        return is_user_logged_in() ? (int) get_current_user_id() : 0;
    }

    /**
     * Checks if the user explicitly has the 'event_submitter' role.
     */
    public function is_event_submitter_user($user_id = 0) {
        $user_id = $this->resolve_capability_user_id($user_id);
        if ($user_id <= 0) {
            return false;
        }
        
        $user = get_userdata($user_id);
        if (!$user instanceof WP_User) {
            return false;
        }
        
        $roles = array_map('sanitize_key', (array) $user->roles);
        return in_array('event_submitter', $roles, true);
    }

    /**
     * Checks if a user has permission to submit items to the platform.
     */
    public function user_can_submit_events($user_id = 0) {
        $user_id = $this->resolve_capability_user_id($user_id);
        if ($user_id <= 0) {
            return false;
        }

        $platform_mode = sanitize_key((string) get_option('nexdirectory_platform_mode', 'user_generated'));
        if ($platform_mode === 'single_organisation' && !user_can($user_id, 'manage_options')) {
            return false;
        }
        
        return user_can($user_id, 'nexdirectory_submit_events')
            || user_can($user_id, 'manage_options');
    }

    /**
     * Checks whether the user is verified for first-submission gating.
     *
     * Backward compatibility: existing users created before verification was introduced
     * may have no verification meta. Treat them as verified to avoid lockouts.
     */
    public function user_verified_for_first_submission($user_id = 0) {
        $user_id = $this->resolve_capability_user_id($user_id);
        if ($user_id <= 0) {
            return false;
        }

        if ((string) get_option('nexdirectory_require_email_verification', '1') !== '1') {
            return true;
        }

        if (user_can($user_id, 'manage_options')) {
            return true;
        }

        $verified = get_user_meta($user_id, 'nexdirectory_email_verified', true);
        if ($verified === '' || $verified === null) {
            return true;
        }

        return (string) $verified === '1';
    }

    /**
     * Checks if a user has basic read access to the platform UI.
     */
    public function user_can_access_platform($user_id = 0) {
        $user_id = $this->resolve_capability_user_id($user_id);
        if ($user_id <= 0) {
            return false;
        }
        
        return user_can($user_id, 'read') || user_can($user_id, 'manage_options');
    }

    /**
     * Checks if a user is a platform moderator or admin.
     */
    public function user_can_moderate_events($user_id = 0) {
        $user_id = $this->resolve_capability_user_id($user_id);
        if ($user_id <= 0) {
            return false;
        }
        
        return user_can($user_id, 'nexdirectory_moderate_events')
            || user_can($user_id, 'manage_options');
    }

    /**
     * Checks if a user is allowed to edit a specific post.
     */
    public function user_can_edit_event($post_id, $user_id = 0) {
        $post_id = absint((int) $post_id);
        if ($post_id <= 0 || get_post_type($post_id) !== $this->post_type) {
            return false;
        }

        if ($this->is_demo_event_edit_locked($post_id, $user_id)) {
            return false;
        }
        
        $user_id = $this->resolve_capability_user_id($user_id);
        if ($user_id <= 0) {
            return false;
        }

        if ((int) get_post_field('post_author', $post_id) === $user_id || $this->user_can_moderate_events($user_id)) {
            return true;
        }

        return (bool) apply_filters(
            'nexdirectory_user_can_edit_event_via_extension',
            false,
            $post_id,
            $user_id
        );
    }

    /**
     * Returns true when a post is demo-seeded and marked as read-only.
     */
    public function is_demo_event_locked($post_id) {
        $post_id = absint((int) $post_id);
        if ($post_id <= 0 || get_post_type($post_id) !== $this->post_type) {
            return false;
        }
        $explicit_lock = (string) get_post_meta($post_id, 'nexdirectory_demo_locked', true);
        if ($explicit_lock === '1') {
            return true;
        }
        if ($explicit_lock === '0') {
            return false;
        }

        // Backward compatibility for demo imports created before lock meta existed.
        return (string) get_post_meta($post_id, 'nexdirectory_demo_imported', true) === '1';
    }

    /**
     * Demo-seeded events remain visible and editable in the free core.
     */
    public function is_demo_event_edit_locked($post_id, $user_id = 0) {
        return false;
    }

    /**
     * Checks whether the current actor can duplicate a listing into an editable copy.
     */
    public function user_can_duplicate_event($post_id, $user_id = 0) {
        $post_id = absint((int) $post_id);
        if ($post_id <= 0 || get_post_type($post_id) !== $this->post_type) {
            return false;
        }

        $user_id = $this->resolve_capability_user_id($user_id);
        if ($user_id <= 0) {
            return false;
        }

        if ($this->user_can_edit_event($post_id, $user_id)) {
            return true;
        }

        if ($this->is_demo_event_edit_locked($post_id, $user_id)) {
            return $this->user_can_submit_events($user_id)
                || $this->user_can_moderate_events($user_id)
                || user_can($user_id, 'edit_posts');
        }

        return user_can($user_id, 'edit_post', $post_id);
    }

    /**
     * Allows internal workflows to bypass the locked-demo mutation guard.
     */
    private function should_bypass_locked_demo_mutation_guard($post_id, $user_id = 0, $context = '') {
        return (bool) apply_filters(
            'nexdirectory_allow_locked_demo_event_mutation',
            false,
            absint((int) $post_id),
            absint((int) $user_id),
            sanitize_key((string) $context)
        );
    }

    /**
     * Returns true when mutating the post should be blocked by demo lock rules.
     */
    private function is_demo_event_mutation_blocked($post_id, $user_id = 0, $context = '') {
        $post_id = absint((int) $post_id);
        if ($post_id <= 0 || get_post_type($post_id) !== $this->post_type) {
            return false;
        }
        if (!$this->is_demo_event_edit_locked($post_id, $user_id)) {
            return false;
        }
        $context = sanitize_key((string) $context);
        $user_id = absint((int) $user_id);
        if (in_array($context, ['pre_trash_post', 'pre_delete_post', 'map_meta_cap_delete_post', 'map_meta_cap_edit_post'], true)
            && $user_id > 0
            && user_can($user_id, 'manage_options')) {
            return false;
        }
        return !$this->should_bypass_locked_demo_mutation_guard($post_id, $user_id, $context);
    }

    /**
     * Capability-level enforcement for legacy locked demo events.
     */
    public function filter_map_meta_cap_for_demo_lock($caps, $cap, $user_id, $args) {
        $cap = sanitize_key((string) $cap);
        if (!in_array($cap, ['edit_post', 'delete_post'], true)) {
            return $caps;
        }

        $post_id = isset($args[0]) ? absint((int) $args[0]) : 0;
        if ($post_id <= 0) {
            return $caps;
        }

        if (!$this->is_demo_event_mutation_blocked($post_id, absint((int) $user_id), 'map_meta_cap_' . $cap)) {
            return $caps;
        }

        return ['do_not_allow'];
    }

    /**
     * Hard-stop direct post updates routed through wp_update_post / wp_insert_post.
     */
    public function action_block_locked_demo_pre_post_update($post_id, $post_data = []) {
        $post_id = absint((int) $post_id);
        $user_id = is_user_logged_in() ? (int) get_current_user_id() : 0;
        $post_data = is_array($post_data) ? $post_data : [];
        if ($user_id > 0 && user_can($user_id, 'manage_options')) {
            $requested_status = sanitize_key((string) ($post_data['post_status'] ?? ''));
            if ($requested_status === 'trash') {
                return;
            }
        }
        if (!$this->is_demo_event_mutation_blocked($post_id, $user_id, 'pre_post_update')) {
            return;
        }

        wp_die(
            esc_html__('This demo event is currently read-only. Duplicate it to create an editable copy.', 'nexdirectory'),
            esc_html__('Read-only demo event', 'nexdirectory'),
            [
                'response' => 403,
                'back_link' => true,
            ]
        );
    }

    /**
     * Prevents moving locked demo events to trash when a custom lock is active.
     */
    public function filter_pre_trash_post_for_demo_lock($trash, $post, $previous_status) {
        if ($trash !== null || !$post instanceof WP_Post) {
            return $trash;
        }

        $user_id = is_user_logged_in() ? (int) get_current_user_id() : 0;
        if (!$this->is_demo_event_mutation_blocked((int) $post->ID, $user_id, 'pre_trash_post')) {
            return $trash;
        }

        return false;
    }

    /**
     * Prevents permanent deletion of locked demo events when a custom lock is active.
     */
    public function filter_pre_delete_post_for_demo_lock($delete, $post, $force_delete) {
        if ($delete !== null || !$post instanceof WP_Post) {
            return $delete;
        }

        $user_id = is_user_logged_in() ? (int) get_current_user_id() : 0;
        if (!$this->is_demo_event_mutation_blocked((int) $post->ID, $user_id, 'pre_delete_post')) {
            return $delete;
        }

        return false;
    }

    /**
     * Checks if a user has permission to manage other organizers.
     */
    public function user_can_manage_organizers($user_id = 0) {
        $user_id = $this->resolve_capability_user_id($user_id);
        if ($user_id <= 0) {
            return false;
        }
        
        return user_can($user_id, 'nexdirectory_manage_organizers')
            || user_can($user_id, 'list_users')
            || user_can($user_id, 'promote_users')
            || user_can($user_id, 'manage_options');
    }

    /**
     * Checks if a user has been explicitly flagged as a trusted editor.
     */
    public function user_is_trusted_editor($user_id = 0) {
        $user_id = $this->resolve_capability_user_id($user_id);
        if ($user_id <= 0) {
            return false;
        }
        
        return (string) get_user_meta($user_id, 'nexdirectory_trusted_editor', true) === '1';
    }

    /**
     * Checks if a user's edits bypass the standard moderation queue.
     */
    public function user_can_bypass_edit_approval($user_id = 0) {
        $user_id = $this->resolve_capability_user_id($user_id);
        if ($user_id <= 0) {
            return false;
        }
        
        return $this->user_can_moderate_events($user_id) || $this->user_is_trusted_editor($user_id);
    }

    /**
     * Determines if a user has at least one published listing.
     */
    public function user_has_published_listings($user_id = 0) {
        $user_id = $this->resolve_capability_user_id($user_id);
        if ($user_id <= 0) {
            return false;
        }

        $posts = get_posts([
            'post_type' => $this->post_type,
            'author' => $user_id,
            'post_status' => 'publish',
            'posts_per_page' => 1,
            'fields' => 'ids',
            'no_found_rows' => true,
            'suppress_filters' => false,
        ]);

        return !empty($posts);
    }

    /**
     * Checks if the user has opted in to organizer marketing/communications.
     */
    public function user_allows_organizer_communications($user_id = 0) {
        $user_id = $this->resolve_capability_user_id($user_id);
        if ($user_id <= 0) {
            return false;
        }

        $stored = get_user_meta($user_id, 'nexdirectory_gdpr_marketing_opt_in', true);
        if ($stored === '' || $stored === null) {
            // Preserve only an explicit legacy opt-in; missing consent always defaults to off.
            return (string) get_user_meta($user_id, 'nexdirectory_marketing_opt_in', true) === '1';
        }
        return (string) $stored === '1';
    }

    /**
     * Updates the user's communication consent flag.
     */
    public function update_user_organizer_communication_consent($user_id = 0, $consented = false) {
        $user_id = $this->resolve_capability_user_id($user_id);
        if ($user_id <= 0) {
            return;
        }
        update_user_meta($user_id, 'nexdirectory_gdpr_marketing_opt_in', $consented ? '1' : '0');
        delete_user_meta($user_id, 'nexdirectory_marketing_opt_in');
    }

    /**
     * Checks if event-owner message CTA is enabled for this user's event pages.
     */
    public function user_allows_event_messages($user_id = 0) {
        $user_id = $this->resolve_capability_user_id($user_id);
        if ($user_id <= 0) {
            return false;
        }

        $stored = get_user_meta($user_id, 'nexdirectory_event_messages_enabled', true);
        if ($stored === '' || $stored === null) {
            // Backward compatibility: existing accounts keep messages enabled by default.
            return true;
        }
        return (string) $stored === '1';
    }

    /**
     * Updates whether event-owner pages show the message CTA.
     */
    public function update_user_event_message_preference($user_id = 0, $enabled = true) {
        $user_id = $this->resolve_capability_user_id($user_id);
        if ($user_id <= 0) {
            return;
        }
        update_user_meta($user_id, 'nexdirectory_event_messages_enabled', $enabled ? '1' : '0');
    }

    /**
     * Returns true for the free core; retained for legacy checks.
     */
    public function are_core_features_available() {
        return (bool) apply_filters('nexdirectory_core_features_available', true);
    }

    /**
     * Standalone WordPress.org build has no posting allowance limit.
     */
    public function get_demo_event_limit() {
        return 0;
    }

    /**
     * Counts editable events for reporting (excludes locked demo showcase entries).
     */
    public function get_created_events_count() {
        global $wpdb;
        if (!$wpdb instanceof wpdb) {
            return 0;
        }

        $query = $wpdb->prepare(
            "SELECT COUNT(1)
             FROM {$wpdb->posts} p
             LEFT JOIN {$wpdb->postmeta} pm_lock
               ON pm_lock.post_id = p.ID
              AND pm_lock.meta_key = %s
             LEFT JOIN {$wpdb->postmeta} pm_demo
               ON pm_demo.post_id = p.ID
              AND pm_demo.meta_key = %s
             WHERE p.post_type = %s
               AND p.post_status NOT IN ('trash', 'auto-draft', 'inherit')
               AND (
                    pm_lock.meta_value = '0'
                    OR (
                        (pm_lock.meta_value IS NULL OR pm_lock.meta_value <> '1')
                        AND (pm_demo.meta_value IS NULL OR pm_demo.meta_value <> '1')
                    )
               )",
            'nexdirectory_demo_locked',
            'nexdirectory_demo_imported',
            $this->post_type
        );
        if (!is_string($query) || $query === '') {
            return 0;
        }

        $count = (int) $wpdb->get_var($query); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared,WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching
        return max(0, $count);
    }

    /**
     * Returns normalized free-plan status used by admin UX.
     *
     * @return array<string, mixed>
     */
    public function get_demo_event_limit_status() {
        $used = $this->get_created_events_count();

        return [
            'enabled' => false,
            'core_features_available' => true,
            'pro_addon_active' => false,
            'pro_license_active' => false,
            'unlimited' => true,
            'limit' => 0,
            'used' => $used,
            'remaining' => null,
            'reached' => false,
        ];
    }

    /**
     * Checks whether a new event can be created under current plan restrictions.
     */
    public function can_create_more_events($user_id = 0, $additional_events = 1) {
        $user_id = $this->resolve_capability_user_id($user_id);
        if ($user_id <= 0) {
            return false;
        }

        return true;
    }

    /**
     * Marks a user as having accepted the GDPR terms.
     */
    public function mark_user_gdpr_terms_accepted($user_id = 0) {
        $user_id = $this->resolve_capability_user_id($user_id);
        if ($user_id <= 0) {
            return;
        }
        update_user_meta($user_id, 'nexdirectory_gdpr_terms_accepted', '1');
        update_user_meta($user_id, 'nexdirectory_gdpr_terms_accepted_at', (string) current_time('timestamp', true));
    }
}
