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

/**
 * Service for handling user roles, capabilities, and platform permissions.
 */
class EEP_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('eep_platform_mode', 'user_generated'));
        if ($platform_mode === 'single_organisation' && !user_can($user_id, 'manage_options')) {
            return false;
        }
        
        return user_can($user_id, 'eep_submit_events')
            || user_can($user_id, 'upload_files')
            || user_can($user_id, 'edit_posts')
            || 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('eep_require_email_verification', '1') !== '1') {
            return true;
        }

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

        $verified = get_user_meta($user_id, 'eep_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, 'eep_moderate_events')
            || user_can($user_id, 'edit_others_posts')
            || 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 $this->user_is_active_event_team_editor($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, 'eep_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, 'eep_demo_imported', true) === '1';
    }

    /**
     * Demo-seeded events remain visible, but editing is locked while premium is inactive.
     */
    public function is_demo_event_edit_locked($post_id, $user_id = 0) {
        $post_id = absint((int) $post_id);
        if ($post_id <= 0 || !$this->is_demo_event_locked($post_id)) {
            return false;
        }
        return !$this->is_premium_unlocked();
    }

    /**
     * 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(
            'eep_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;
        }
        return !$this->should_bypass_locked_demo_mutation_guard($post_id, $user_id, $context);
    }

    /**
     * Capability-level enforcement for locked demo events on free mode.
     */
    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;
        if (!$this->is_demo_event_mutation_blocked($post_id, $user_id, 'pre_post_update')) {
            return;
        }

        wp_die(
            esc_html__('This demo event is read-only in free mode. 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 while premium is inactive.
     */
    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 while premium is inactive.
     */
    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;
    }

    /**
     * Team members with active owner/editor role can edit the listing.
     */
    private function user_is_active_event_team_editor($post_id, $user_id) {
        $post_id = absint((int) $post_id);
        $user_id = absint((int) $user_id);
        if ($post_id <= 0 || $user_id <= 0) {
            return false;
        }

        $team_rows = get_post_meta($post_id, 'eep_event_team_members', true);
        if (is_string($team_rows) && $team_rows !== '') {
            $decoded = json_decode($team_rows, true);
            if (is_array($decoded)) {
                $team_rows = $decoded;
            }
        }
        if (!is_array($team_rows) || empty($team_rows)) {
            return false;
        }

        $user = get_userdata($user_id);
        $user_email = $user instanceof WP_User ? strtolower(sanitize_email((string) $user->user_email)) : '';

        foreach ($team_rows as $row) {
            if (!is_array($row)) {
                continue;
            }
            $status = sanitize_key((string) ($row['status'] ?? 'invited'));
            $role = sanitize_key((string) ($row['role'] ?? 'support'));
            if ($status !== 'active' || !in_array($role, ['owner', 'editor'], true)) {
                continue;
            }
            $scope_website_raw = $row['scope_website'] ?? ((is_array($row['scopes'] ?? null) && array_key_exists('website', (array) $row['scopes'])) ? $row['scopes']['website'] : '1');
            $scope_website_value = strtolower(trim((string) $scope_website_raw));
            $scope_website_enabled = !in_array($scope_website_value, ['0', 'false', 'no', 'off'], true);
            if (!$scope_website_enabled) {
                continue;
            }
            $row_user_id = absint((int) ($row['user_id'] ?? 0));
            if ($row_user_id > 0 && $row_user_id === $user_id) {
                return true;
            }
            $row_email = strtolower(sanitize_email((string) ($row['email'] ?? '')));
            if ($row_user_id <= 0 && $row_email !== '' && $user_email !== '' && hash_equals($row_email, $user_email)) {
                return true;
            }
        }

        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, 'eep_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, 'eep_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' => true,
        ]);

        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, 'eep_gdpr_marketing_opt_in', true);
        if ($stored === '' || $stored === null) {
            // Backward compatibility: existing accounts remain opted in until they change this setting.
            return true;
        }
        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, 'eep_gdpr_marketing_opt_in', $consented ? '1' : '0');
    }

    /**
     * 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, 'eep_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, 'eep_event_messages_enabled', $enabled ? '1' : '0');
    }

    /**
     * Returns true when premium code is available (licensed/trial/unlocked).
     */
    public function is_premium_unlocked() {
        if (!function_exists('epw_fs')) {
            return true;
        }

        try {
            return (bool) epw_fs()->can_use_premium_code__premium_only();
        } catch (Throwable $unused) {
            return false;
        }
    }

    /**
     * Maximum number of events allowed in free demo mode.
     */
    public function get_demo_event_limit() {
        $limit = (int) apply_filters('eep_demo_event_limit', 3);
        if ($limit <= 0) {
            $limit = 3;
        }
        return $limit;
    }

    /**
     * Counts editable events under free plan limits (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')
                    )
               )",
            'eep_demo_locked',
            'eep_demo_imported',
            $this->post_type
        );
        if (!is_string($query) || $query === '') {
            return 0;
        }

        $count = (int) $wpdb->get_var($query);
        return max(0, $count);
    }

    /**
     * Returns normalized demo-limit status used by admin/frontend UX.
     *
     * @return array<string, mixed>
     */
    public function get_demo_event_limit_status() {
        $premium_unlocked = $this->is_premium_unlocked();
        $limit = $this->get_demo_event_limit();
        $used = $this->get_created_events_count();
        $remaining = max(0, $limit - $used);
        $enabled = !$premium_unlocked;
        $reached = $enabled && $used >= $limit;

        return [
            'enabled' => $enabled,
            'premium_unlocked' => $premium_unlocked,
            'limit' => $limit,
            'used' => $used,
            'remaining' => $remaining,
            'reached' => $reached,
        ];
    }

    /**
     * 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;
        }

        $status = $this->get_demo_event_limit_status();
        if (empty($status['enabled'])) {
            return true;
        }

        $additional = max(1, (int) $additional_events);
        return ((int) $status['used'] + $additional) <= (int) $status['limit'];
    }

    /**
     * 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, 'eep_gdpr_terms_accepted', '1');
        update_user_meta($user_id, 'eep_gdpr_terms_accepted_at', (string) current_time('timestamp', true));
    }
}
