<?php
declare(strict_types=1);

namespace Help4BuilderSuite\Modules\StaffDirectory;

use Help4BuilderSuite\Contracts\ModuleContract;

if (! defined('ABSPATH')) exit;

final class Module implements ModuleContract
{
    public const CAP_MANAGE = 'manage_help4_staff_directory';

    private string $hook = '';
    public function key(): string { return 'staff_directory'; }

    public function boot(): void
    {
        add_action('init', [$this, 'ensureCapabilities'], 5);
        add_action('admin_menu', [$this, 'menu']);
        add_action('admin_enqueue_scripts', [$this, 'assets']);
        add_action('wp_enqueue_scripts', [$this, 'frontendAssets']);
        add_action('wp_ajax_h4bs_staff_save', [$this, 'save']);
        add_action('wp_ajax_h4bs_staff_recover', [$this, 'recover']);
    }

    public function menu(): void
    {
        $this->hook = (string) add_submenu_page('h4bs-settings', __('Staff Directory', 'help4-builder-suite'), __('Staff Directory', 'help4-builder-suite'), self::CAP_MANAGE, 'h4bs-staff-directory', [$this, 'page']);
    }

    /** Give administrators Staff Directory access without requiring every editor to be an administrator. */
    public function ensureCapabilities(): void
    {
        $role = get_role('administrator');
        if ($role && ! $role->has_cap(self::CAP_MANAGE)) $role->add_cap(self::CAP_MANAGE);
    }

    public function assets(string $hook): void
    {
        if ($hook !== $this->hook) return;
        wp_enqueue_media();
        wp_enqueue_style('h4bs-staff-admin', H4BS_URL . 'assets/css/staff-directory-admin.css', [], H4BS_VERSION);
        wp_enqueue_script('h4bs-staff-admin', H4BS_URL . 'assets/js/staff-directory-admin.js', ['jquery'], H4BS_VERSION, ['in_footer' => true, 'strategy' => 'defer']);
        wp_localize_script('h4bs-staff-admin', 'h4bsStaffData', [
            'ajaxUrl' => admin_url('admin-ajax.php'),
            'nonce' => wp_create_nonce('h4bs_staff_directory'),
            'workspace' => Workspace::load(),
            'tiers' => ['owner' => __('Owner / Founder', 'help4-builder-suite'), 'leadership' => __('Directors & Leadership', 'help4-builder-suite'), 'faculty' => __('Faculty / Teaching Artists', 'help4-builder-suite'), 'guest' => __('Guest Faculty', 'help4-builder-suite'), 'operations' => __('Production / Operations', 'help4-builder-suite')],
            'recovery' => array_map([$this, 'recoveryPayload'], array_values(array_filter((array) get_option('h4bs_staff_directory_recovery', []), 'is_array'))),
        ]);
    }

    public function frontendAssets(): void
    {
        if (! $this->needsFrontendAssets()) return;
        wp_enqueue_style('h4bs-staff-directory', H4BS_URL . 'assets/css/staff-directory.css', [], H4BS_VERSION);
        wp_enqueue_script('h4bs-staff-directory', H4BS_URL . 'assets/js/staff-directory.js', [], H4BS_VERSION, ['in_footer' => true, 'strategy' => 'defer']);
    }

    private function needsFrontendAssets(): bool
    {
        if (! is_singular()) return false;
        $postId = get_queried_object_id();
        if ($postId < 1) return false;
        $layout = (string) get_post_meta($postId, '_h4_builder_layout', true);
        if ($layout !== '' && preg_match('/["\']type["\']\s*:\s*["\']staff_directory["\']/', $layout) === 1) return true;
        $content = (string) get_post_field('post_content', $postId);
        return has_shortcode($content, 'h4_staff_directory') || has_shortcode($content, 'h4bs_staff_directory');
    }

    public function page(): void
    {
        if (! current_user_can(self::CAP_MANAGE)) wp_die(esc_html__('You cannot manage staff profiles.', 'help4-builder-suite'));
        ?>
        <div class="wrap h4bs-staff-admin" data-h4bs-staff-admin>
            <header class="h4bs-staff-header"><div><h1><?php esc_html_e('Teachers & team', 'help4-builder-suite'); ?></h1><p><?php esc_html_e('Add people, update public profiles, and arrange the order visitors see.', 'help4-builder-suite'); ?></p></div><div class="h4bs-staff-commands"><span role="status" aria-live="polite" data-staff-save-state><?php esc_html_e('All changes saved', 'help4-builder-suite'); ?></span><button class="button" type="button" data-staff-undo disabled><?php esc_html_e('Undo', 'help4-builder-suite'); ?></button><button class="button" type="button" data-staff-redo disabled><?php esc_html_e('Redo', 'help4-builder-suite'); ?></button><button class="button button-primary" type="button" data-staff-save hidden><?php esc_html_e('Save ordering', 'help4-builder-suite'); ?></button></div></header>
            <nav class="nav-tab-wrapper" aria-label="<?php esc_attr_e('Staff directory sections', 'help4-builder-suite'); ?>"><button class="nav-tab nav-tab-active" type="button" data-staff-tab="profiles"><?php esc_html_e('Profiles', 'help4-builder-suite'); ?></button><button class="nav-tab" type="button" data-staff-tab="presentation"><?php esc_html_e('Presentation', 'help4-builder-suite'); ?></button><button class="nav-tab" type="button" data-staff-tab="recovery"><?php esc_html_e('Recovery', 'help4-builder-suite'); ?></button></nav>
            <main><section data-staff-panel="profiles"><div class="h4bs-staff-toolbar"><label><span><?php esc_html_e('Search', 'help4-builder-suite'); ?></span><input type="search" data-staff-search></label><label><span><?php esc_html_e('Tier', 'help4-builder-suite'); ?></span><select data-staff-tier-filter><option value=""><?php esc_html_e('All tiers', 'help4-builder-suite'); ?></option></select></label><label><span><?php esc_html_e('State', 'help4-builder-suite'); ?></span><select data-staff-state-filter><option value=""><?php esc_html_e('All states', 'help4-builder-suite'); ?></option><option value="published"><?php esc_html_e('Published', 'help4-builder-suite'); ?></option><option value="draft"><?php esc_html_e('Draft', 'help4-builder-suite'); ?></option><option value="hidden"><?php esc_html_e('Hidden', 'help4-builder-suite'); ?></option><option value="archived"><?php esc_html_e('Archived', 'help4-builder-suite'); ?></option></select></label><label><span><?php esc_html_e('Selected profiles', 'help4-builder-suite'); ?></span><select data-staff-bulk-action><option value=""><?php esc_html_e('Choose action', 'help4-builder-suite'); ?></option><option value="published"><?php esc_html_e('Publish', 'help4-builder-suite'); ?></option><option value="draft"><?php esc_html_e('Move to draft', 'help4-builder-suite'); ?></option><option value="hidden"><?php esc_html_e('Hide', 'help4-builder-suite'); ?></option><option value="archived"><?php esc_html_e('Archive', 'help4-builder-suite'); ?></option></select></label><button class="button" type="button" data-staff-bulk-apply><?php esc_html_e('Apply to selected', 'help4-builder-suite'); ?></button><button class="button button-primary" type="button" data-staff-add><?php esc_html_e('Add profile', 'help4-builder-suite'); ?></button></div><div data-staff-list></div><p data-staff-empty hidden><?php esc_html_e('No profiles match this view.', 'help4-builder-suite'); ?></p></section>
            <section data-staff-panel="presentation" hidden><h2><?php esc_html_e('Responsive presentation', 'help4-builder-suite'); ?></h2><div data-staff-presentation></div><div data-staff-preview></div></section>
            <section data-staff-panel="recovery" hidden><h2><?php esc_html_e('Recovery', 'help4-builder-suite'); ?></h2><p><?php esc_html_e('Changed saves keep five prior revisions. Restoring never deletes uploaded media.', 'help4-builder-suite'); ?></p><div data-staff-recovery></div></section></main>
            <dialog data-staff-editor><form method="dialog" data-staff-editor-form><header><h2 data-staff-editor-heading><?php esc_html_e('Edit staff profile', 'help4-builder-suite'); ?></h2><button class="button-link" type="button" data-staff-editor-close aria-label="<?php esc_attr_e('Close profile editor', 'help4-builder-suite'); ?>">×</button></header><div data-staff-validation role="alert" tabindex="-1" hidden></div><div data-staff-editor-fields></div><footer><span role="status" aria-live="polite" data-staff-editor-save-state></span><button class="button" type="button" data-staff-editor-cancel><?php esc_html_e('Cancel', 'help4-builder-suite'); ?></button><button class="button button-primary" type="submit"><?php esc_html_e('Save profile', 'help4-builder-suite'); ?></button></footer></form></dialog>
        </div><?php
    }

    public function save(): void
    {
        $this->authorize();
        // phpcs:disable WordPress.Security.NonceVerification.Missing -- authorize() verifies the AJAX nonce and capability before request reads.
        // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Structured JSON is decoded then fully normalized by Workspace::sanitize().
        $workspaceJson = isset($_POST['workspace']) ? wp_unslash((string) $_POST['workspace']) : '';
        $decoded = json_decode($workspaceJson, true);
        if (! is_array($decoded)) wp_send_json_error(['message' => __('Invalid staff directory data.', 'help4-builder-suite')], 422);
        $requestId = sanitize_key(wp_unslash((string) ($_SERVER['HTTP_X_H4BS_REQUEST_ID'] ?? '')));
        $userId = get_current_user_id();
        $expected = max(1, absint(wp_unslash($_POST['revision'] ?? '0')));
        $confirmation = isset($_POST['owner_confirmation']) ? sanitize_text_field(wp_unslash((string) $_POST['owner_confirmation'])) : '';
        $requestHash = hash('sha256', $workspaceJson . '|' . (string) $expected . '|' . $confirmation);
        $replays = array_values(array_filter((array) get_user_meta($userId, '_h4bs_staff_save_replays', true), 'is_array'));
        if ($requestId !== '') {
            foreach ($replays as $replay) {
                if (($replay['id'] ?? '') !== $requestId) continue;
                if (! hash_equals((string) ($replay['hash'] ?? ''), $requestHash)) wp_send_json_error(['message' => __('This save request was reused with different changes. Review the latest version.', 'help4-builder-suite')], 409);
                wp_send_json_success(['workspace' => $replay['workspace'], 'message' => __('Staff directory saved. Previous response replayed safely.', 'help4-builder-suite')]);
            }
        }
        $current = Workspace::load();
        if ($expected !== (int) $current['revision']) wp_send_json_error(['message' => __('The directory changed in another tab. Reload before saving.', 'help4-builder-suite'), 'workspace' => $current], 409);
        $currentOwner = $this->owner($current['profiles']);
        $next = Workspace::sanitize($decoded);
        $nextOwner = $this->owner($next['profiles']);
        if ($currentOwner && (! $nextOwner || $nextOwner['id'] !== $currentOwner['id'])) {
            $required = 'TRANSFER ' . $currentOwner['name'];
            if (! hash_equals($required, $confirmation)) {
                /* translators: %s: exact owner-transfer confirmation phrase. */
                wp_send_json_error(['message' => sprintf(__('Owner protection is active. Enter "%s" to transfer ownership.', 'help4-builder-suite'), $required)], 422);
            }
        }
        $history = array_values(array_filter((array) get_option('h4bs_staff_directory_recovery', []), 'is_array'));
        array_unshift($history, ['created_at' => current_time('mysql', true), 'user_id' => get_current_user_id(), 'workspace' => $current]);
        update_option('h4bs_staff_directory_recovery', array_slice($history, 0, 5), false);
        $next['revision'] = $expected + 1;
        $next['updated_at'] = current_time('mysql', true);
        update_option(Workspace::OPTION, $next, false);
        do_action('h4bs_staff_directory_saved', $next, $current);
        if ($requestId !== '') {
            array_unshift($replays, ['id' => $requestId, 'hash' => $requestHash, 'workspace' => $next, 'created_at' => time()]);
            update_user_meta($userId, '_h4bs_staff_save_replays', array_slice($replays, 0, 10));
        }
        wp_send_json_success(['workspace' => $next, 'message' => __('Staff directory saved.', 'help4-builder-suite')]);
        // phpcs:enable WordPress.Security.NonceVerification.Missing
    }

    public function recover(): void
    {
        $this->authorize();
        // phpcs:disable WordPress.Security.NonceVerification.Missing -- authorize() verifies the AJAX nonce and capability before request reads.
        $history = array_values(array_filter((array) get_option('h4bs_staff_directory_recovery', []), 'is_array'));
        $index = isset($_POST['index']) ? absint(wp_unslash($_POST['index'])) : 0;
        $entry = $history[$index] ?? null;
        if (! is_array($entry) || ! is_array($entry['workspace'] ?? null)) wp_send_json_error(['message' => __('Recovery point unavailable.', 'help4-builder-suite')], 404);
        $current = Workspace::load();
        array_unshift($history, ['created_at' => current_time('mysql', true), 'user_id' => get_current_user_id(), 'workspace' => $current, 'reason' => 'before_restore']);
        update_option('h4bs_staff_directory_recovery', array_slice($history, 0, 5), false);
        $clean = Workspace::sanitize($entry['workspace']);
        $clean['revision'] = (int) $current['revision'] + 1;
        $clean['updated_at'] = current_time('mysql', true);
        update_option(Workspace::OPTION, $clean, false);
        wp_send_json_success(['workspace' => $clean, 'message' => __('Staff directory recovery loaded and saved.', 'help4-builder-suite')]);
        // phpcs:enable WordPress.Security.NonceVerification.Missing
    }

    /** @param array<string, mixed> $entry @return array<string, mixed> */
    private function recoveryPayload(array $entry): array
    {
        $user = get_userdata(absint($entry['user_id'] ?? 0));
        return [
            'created_at' => sanitize_text_field((string) ($entry['created_at'] ?? '')),
            'actor_name' => $user ? sanitize_text_field($user->display_name) : '',
            'workspace' => is_array($entry['workspace'] ?? null) ? Workspace::sanitize($entry['workspace']) : [],
        ];
    }

    /** @param array<int, array<string, mixed>> $profiles @return array<string, mixed>|null */
    private function owner(array $profiles): ?array
    {
        foreach ($profiles as $profile) if (($profile['tier'] ?? '') === 'owner') return $profile;
        return null;
    }

    private function authorize(): void
    {
        check_ajax_referer('h4bs_staff_directory', 'nonce');
        if (! current_user_can(self::CAP_MANAGE)) wp_send_json_error(['message' => __('Permission denied.', 'help4-builder-suite')], 403);
    }
}
