<?php
declare(strict_types=1);

namespace Help4BuilderSuite\Modules\Redirection;

use Help4BuilderSuite\Contracts\ModuleContract;
use Help4BuilderSuite\Support\AdminRequest;
use Help4BuilderSuite\Support\IpTools;

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

// Redirect admin filters are read-only; redirect/404 logs live in dedicated plugin tables.
// phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching

final class Module implements ModuleContract
{
    public function key(): string
    {
        return 'redirection';
    }

    public function boot(): void
    {
        add_action('admin_menu', [$this, 'registerAdminPage']);
        add_action('admin_post_h4bs_save_redirect', [$this, 'handleSaveRedirect']);
        add_action('admin_post_h4bs_delete_redirect', [$this, 'handleDeleteRedirect']);
        add_action('admin_post_h4bs_clear_404_logs', [$this, 'handleClearLogs']);

        add_action('template_redirect', [$this, 'applyRedirects'], 1);
        add_action('template_redirect', [$this, 'logNotFoundHit'], 9999);
    }

    public function registerAdminPage(): void
    {
        add_submenu_page(
            'h4bs-settings',
            __('Redirects + 404 Logs', 'help4-builder-suite'),
            __('Redirects', 'help4-builder-suite'),
            'manage_options',
            'h4bs-redirection',
            [$this, 'renderAdminPage']
        );
    }

    public function renderAdminPage(): void
    {
        if (! current_user_can('manage_options')) {
            return;
        }

        global $wpdb;
        $rules = $wpdb->get_results($wpdb->prepare('SELECT * FROM %i ORDER BY id DESC LIMIT 300', $this->redirectsTable()), ARRAY_A);
        $logs = $wpdb->get_results($wpdb->prepare('SELECT * FROM %i ORDER BY last_seen DESC LIMIT 300', $this->notFoundTable()), ARRAY_A);
        if (! is_array($rules)) {
            $rules = [];
        }
        if (! is_array($logs)) {
            $logs = [];
        }

        $sourcePrefill = AdminRequest::queryText('source');
        ?>
        <div class="wrap h4bs-admin-wrap">
            <h1><?php esc_html_e('Redirect Manager + 404 Logs', 'help4-builder-suite'); ?></h1>
            <p><?php esc_html_e('301 Redirect Pro style workflow: create rules and convert 404 hits into redirects fast.', 'help4-builder-suite'); ?></p>
            <?php $this->renderAdminNotice(); ?>

            <h2><?php esc_html_e('Add / Update Redirect', 'help4-builder-suite'); ?></h2>
            <form method="post" action="<?php echo esc_url(admin_url('admin-post.php')); ?>">
                <?php wp_nonce_field('h4bs_save_redirect', 'h4bs_redirect_nonce'); ?>
                <input type="hidden" name="action" value="h4bs_save_redirect">
                <table class="form-table" role="presentation">
                    <tbody>
                        <tr>
                            <th><label for="h4bs-source-path"><?php esc_html_e('Source Path', 'help4-builder-suite'); ?></label></th>
                            <td><input id="h4bs-source-path" class="regular-text code" type="text" name="source_path" required value="<?php echo esc_attr($sourcePrefill); ?>" placeholder="/old-page"></td>
                        </tr>
                        <tr>
                            <th><label for="h4bs-match-type"><?php esc_html_e('Match Type', 'help4-builder-suite'); ?></label></th>
                            <td>
                                <select id="h4bs-match-type" name="match_type">
                                    <option value="exact"><?php esc_html_e('Exact Path', 'help4-builder-suite'); ?></option>
                                    <option value="prefix"><?php esc_html_e('Path Prefix', 'help4-builder-suite'); ?></option>
                                    <option value="wildcard"><?php esc_html_e('Wildcard (*)', 'help4-builder-suite'); ?></option>
                                    <option value="regex"><?php esc_html_e('Regex', 'help4-builder-suite'); ?></option>
                                </select>
                            </td>
                        </tr>
                        <tr>
                            <th><label for="h4bs-target-url"><?php esc_html_e('Target URL', 'help4-builder-suite'); ?></label></th>
                            <td><input id="h4bs-target-url" class="regular-text code" type="url" name="target_url" required placeholder="https://example.com/new-page"></td>
                        </tr>
                        <tr>
                            <th><label for="h4bs-http-code"><?php esc_html_e('HTTP Code', 'help4-builder-suite'); ?></label></th>
                            <td>
                                <select id="h4bs-http-code" name="http_code">
                                    <option value="301">301</option>
                                    <option value="302">302</option>
                                    <option value="307">307</option>
                                    <option value="308">308</option>
                                </select>
                            </td>
                        </tr>
                        <tr>
                            <th><label for="h4bs-status"><?php esc_html_e('Status', 'help4-builder-suite'); ?></label></th>
                            <td>
                                <select id="h4bs-status" name="status">
                                    <option value="active"><?php esc_html_e('Active', 'help4-builder-suite'); ?></option>
                                    <option value="inactive"><?php esc_html_e('Inactive', 'help4-builder-suite'); ?></option>
                                </select>
                            </td>
                        </tr>
                    </tbody>
                </table>
                <?php submit_button(__('Save Redirect', 'help4-builder-suite')); ?>
            </form>

            <h2><?php esc_html_e('Redirect Rules', 'help4-builder-suite'); ?></h2>
            <table class="widefat striped">
                <thead>
                    <tr>
                        <th><?php esc_html_e('Source', 'help4-builder-suite'); ?></th>
                        <th><?php esc_html_e('Type', 'help4-builder-suite'); ?></th>
                        <th><?php esc_html_e('Target', 'help4-builder-suite'); ?></th>
                        <th><?php esc_html_e('Code', 'help4-builder-suite'); ?></th>
                        <th><?php esc_html_e('Status', 'help4-builder-suite'); ?></th>
                        <th><?php esc_html_e('Hits', 'help4-builder-suite'); ?></th>
                        <th><?php esc_html_e('Actions', 'help4-builder-suite'); ?></th>
                    </tr>
                </thead>
                <tbody>
                    <?php if (! $rules) : ?>
                        <tr><td colspan="7"><?php esc_html_e('No redirect rules yet.', 'help4-builder-suite'); ?></td></tr>
                    <?php endif; ?>
                    <?php foreach ($rules as $rule) : ?>
                        <?php
                        $id = (int) ($rule['id'] ?? 0);
                        $deleteUrl = wp_nonce_url(
                            add_query_arg(['action' => 'h4bs_delete_redirect', 'redirect_id' => $id], admin_url('admin-post.php')),
                            'h4bs_delete_redirect_' . $id
                        );
                        ?>
                        <tr>
                            <td><code><?php echo esc_html((string) ($rule['source_path'] ?? '')); ?></code></td>
                            <td><?php echo esc_html((string) ($rule['match_type'] ?? 'exact')); ?></td>
                            <td><code><?php echo esc_html((string) ($rule['target_url'] ?? '')); ?></code></td>
                            <td><?php echo esc_html((string) ((int) ($rule['http_code'] ?? 301))); ?></td>
                            <td><?php echo esc_html((string) ($rule['status'] ?? 'active')); ?></td>
                            <td><?php echo esc_html((string) ((int) ($rule['hits'] ?? 0))); ?></td>
                            <td><a class="button button-small" href="<?php echo esc_url($deleteUrl); ?>" onclick="return confirm('<?php echo esc_js(__('Delete this redirect?', 'help4-builder-suite')); ?>')"><?php esc_html_e('Delete', 'help4-builder-suite'); ?></a></td>
                        </tr>
                    <?php endforeach; ?>
                </tbody>
            </table>

            <h2 style="margin-top:2rem;"><?php esc_html_e('404 Log', 'help4-builder-suite'); ?></h2>
            <form method="post" action="<?php echo esc_url(admin_url('admin-post.php')); ?>" style="margin-bottom:1rem;">
                <?php wp_nonce_field('h4bs_clear_404_logs', 'h4bs_clear_404_logs_nonce'); ?>
                <input type="hidden" name="action" value="h4bs_clear_404_logs">
                <button type="submit" class="button"><?php esc_html_e('Clear 404 Logs', 'help4-builder-suite'); ?></button>
            </form>
            <table class="widefat striped">
                <thead>
                    <tr>
                        <th><?php esc_html_e('Path', 'help4-builder-suite'); ?></th>
                        <th><?php esc_html_e('Hits', 'help4-builder-suite'); ?></th>
                        <th><?php esc_html_e('Last Seen', 'help4-builder-suite'); ?></th>
                        <th><?php esc_html_e('Referrer', 'help4-builder-suite'); ?></th>
                        <th><?php esc_html_e('Quick Action', 'help4-builder-suite'); ?></th>
                    </tr>
                </thead>
                <tbody>
                    <?php if (! $logs) : ?>
                        <tr><td colspan="5"><?php esc_html_e('No 404 hits logged yet.', 'help4-builder-suite'); ?></td></tr>
                    <?php endif; ?>
                    <?php foreach ($logs as $log) : ?>
                        <?php $path = (string) ($log['request_path'] ?? ''); ?>
                        <tr>
                            <td><code><?php echo esc_html($path); ?></code></td>
                            <td><?php echo esc_html((string) ((int) ($log['hits'] ?? 0))); ?></td>
                            <td><?php echo esc_html((string) ($log['last_seen'] ?? '')); ?></td>
                            <td><code><?php echo esc_html((string) ($log['referer_url'] ?? '')); ?></code></td>
                            <td><a class="button button-small" href="<?php echo esc_url(admin_url('admin.php?page=h4bs-redirection&source=' . rawurlencode($path))); ?>"><?php esc_html_e('Create Redirect', 'help4-builder-suite'); ?></a></td>
                        </tr>
                    <?php endforeach; ?>
                </tbody>
            </table>
        </div>
        <?php
    }

    public function handleSaveRedirect(): void
    {
        if (! current_user_can('manage_options')) {
            wp_die(esc_html__('Unauthorized request.', 'help4-builder-suite'));
        }
        check_admin_referer('h4bs_save_redirect', 'h4bs_redirect_nonce');

        $sourcePath = $this->normalizeSourcePath(sanitize_text_field((string) wp_unslash($_POST['source_path'] ?? '')));
        $matchType = sanitize_key((string) wp_unslash($_POST['match_type'] ?? 'exact'));
        $targetUrl = esc_url_raw((string) wp_unslash($_POST['target_url'] ?? ''));
        $httpCode = (int) sanitize_text_field((string) wp_unslash($_POST['http_code'] ?? 301));
        $status = sanitize_key((string) wp_unslash($_POST['status'] ?? 'active'));

        if ($sourcePath === '' || $targetUrl === '') {
            $this->adminRedirect(['h4bs_notice' => 'redirect_invalid']);
        }
        if (! in_array($matchType, ['exact', 'prefix', 'wildcard', 'regex'], true)) {
            $matchType = 'exact';
        }
        if (! in_array($httpCode, [301, 302, 307, 308], true)) {
            $httpCode = 301;
        }
        if (! in_array($status, ['active', 'inactive'], true)) {
            $status = 'active';
        }

        global $wpdb;
        $wpdb->insert(
            $this->redirectsTable(),
            [
                'source_path' => $sourcePath,
                'match_type' => $matchType,
                'target_url' => $targetUrl,
                'status' => $status,
                'http_code' => $httpCode,
            ],
            ['%s', '%s', '%s', '%s', '%d']
        );
        $this->adminRedirect(['h4bs_notice' => 'redirect_saved']);
    }

    public function handleDeleteRedirect(): void
    {
        if (! current_user_can('manage_options')) {
            wp_die(esc_html__('Unauthorized request.', 'help4-builder-suite'));
        }
        $id = AdminRequest::queryAbsInt('redirect_id');
        if ($id <= 0) {
            $this->adminRedirect(['h4bs_notice' => 'redirect_invalid']);
        }
        check_admin_referer('h4bs_delete_redirect_' . $id);
        global $wpdb;
        $wpdb->delete($this->redirectsTable(), ['id' => $id], ['%d']);
        $this->adminRedirect(['h4bs_notice' => 'redirect_deleted']);
    }

    public function handleClearLogs(): void
    {
        if (! current_user_can('manage_options')) {
            wp_die(esc_html__('Unauthorized request.', 'help4-builder-suite'));
        }
        check_admin_referer('h4bs_clear_404_logs', 'h4bs_clear_404_logs_nonce');
        global $wpdb;
        $wpdb->query($wpdb->prepare('TRUNCATE TABLE %i', $this->notFoundTable()));
        $this->adminRedirect(['h4bs_notice' => 'logs_cleared']);
    }

    public function applyRedirects(): void
    {
        if (is_admin() || wp_doing_ajax() || wp_doing_cron()) {
            return;
        }

        $requestPath = $this->currentRequestPath();
        if ($requestPath === '') {
            return;
        }

        global $wpdb;
        $rules = $wpdb->get_results(
            $wpdb->prepare(
                "SELECT id, source_path, match_type, target_url, http_code
             FROM %i
             WHERE status = 'active'
             ORDER BY id ASC
             LIMIT 500",
                $this->redirectsTable()
            ),
            ARRAY_A
        ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
        if (! is_array($rules) || ! $rules) {
            return;
        }

        foreach ($rules as $rule) {
            $source = (string) ($rule['source_path'] ?? '');
            $matchType = (string) ($rule['match_type'] ?? 'exact');
            if (! $this->pathMatchesRule($requestPath, $source, $matchType)) {
                continue;
            }
            $target = esc_url_raw((string) ($rule['target_url'] ?? ''));
            if ($target === '') {
                continue;
            }
            $id = (int) ($rule['id'] ?? 0);
            if ($id > 0) {
                $wpdb->query($wpdb->prepare('UPDATE %i SET hits = hits + 1, last_hit_at = NOW() WHERE id = %d', $this->redirectsTable(), $id));
            }
            $code = (int) ($rule['http_code'] ?? 301);
            if (! in_array($code, [301, 302, 307, 308], true)) {
                $code = 301;
            }
            $this->sendRedirect($target, $code);
            exit;
        }
    }

    public function logNotFoundHit(): void
    {
        if (is_admin() || wp_doing_ajax() || wp_doing_cron() || ! is_404()) {
            return;
        }
        $path = $this->currentRequestPathWithQuery();
        if ($path === '' || str_starts_with($path, '/wp-json')) {
            return;
        }

        global $wpdb;
        $table = $this->notFoundTable();
        $existingId = (int) $wpdb->get_var($wpdb->prepare('SELECT id FROM %i WHERE request_path = %s LIMIT 1', $table, $path));
        $referrer = esc_url_raw(isset($_SERVER['HTTP_REFERER']) ? (string) wp_unslash($_SERVER['HTTP_REFERER']) : '');
        $ip = $this->requestIp();
        $ua = isset($_SERVER['HTTP_USER_AGENT']) ? sanitize_text_field((string) wp_unslash($_SERVER['HTTP_USER_AGENT'])) : '';

        if ($existingId > 0) {
            $wpdb->query($wpdb->prepare('UPDATE %i SET hits = hits + 1, referer_url = %s, last_ip = %s, user_agent = %s, last_seen = NOW() WHERE id = %d', $table, $referrer, $ip, $ua, $existingId));
            return;
        }

        $wpdb->insert(
            $table,
            [
                'request_path' => $path,
                'referer_url' => $referrer,
                'hits' => 1,
                'last_ip' => $ip,
                'user_agent' => $ua,
            ],
            ['%s', '%s', '%d', '%s', '%s']
        );
    }

    private function renderAdminNotice(): void
    {
        $notice = AdminRequest::queryKey('h4bs_notice');
        if ($notice === '') {
            return;
        }
        $messages = [
            'redirect_saved' => ['success', __('Redirect saved.', 'help4-builder-suite')],
            'redirect_deleted' => ['success', __('Redirect deleted.', 'help4-builder-suite')],
            'redirect_invalid' => ['error', __('Please provide a valid source path and target URL.', 'help4-builder-suite')],
            'logs_cleared' => ['success', __('404 logs cleared.', 'help4-builder-suite')],
        ];
        if (! isset($messages[$notice])) {
            return;
        }
        [$type, $message] = $messages[$notice];
        echo '<div class="notice notice-' . esc_attr($type) . '"><p>' . esc_html($message) . '</p></div>';
    }

    private function adminRedirect(array $args = []): void
    {
        wp_safe_redirect(add_query_arg(array_merge(['page' => 'h4bs-redirection'], $args), admin_url('admin.php')));
        exit;
    }

    private function normalizeSourcePath(string $source): string
    {
        $source = trim($source);
        if ($source === '') {
            return '';
        }
        $parsed = wp_parse_url($source);
        if (is_array($parsed) && isset($parsed['path'])) {
            $source = (string) $parsed['path'];
            if (! empty($parsed['query'])) {
                $source .= '?' . (string) $parsed['query'];
            }
        }
        if (! str_starts_with($source, '/')) {
            $source = '/' . $source;
        }
        return $source;
    }

    private function currentRequestPath(): string
    {
        $uri = isset($_SERVER['REQUEST_URI']) ? sanitize_text_field((string) wp_unslash($_SERVER['REQUEST_URI'])) : '';
        if ($uri === '') {
            return '';
        }
        $path = (string) wp_parse_url($uri, PHP_URL_PATH);
        if ($path === '') {
            return '';
        }
        return '/' . ltrim($path, '/');
    }

    private function currentRequestPathWithQuery(): string
    {
        $path = $this->currentRequestPath();
        if ($path === '') {
            return '';
        }
        $requestUri = isset($_SERVER['REQUEST_URI']) ? sanitize_text_field((string) wp_unslash($_SERVER['REQUEST_URI'])) : '';
        $query = (string) wp_parse_url($requestUri, PHP_URL_QUERY);
        return $query !== '' ? $path . '?' . $query : $path;
    }

    private function pathMatchesRule(string $requestPath, string $source, string $matchType): bool
    {
        if ($source === '') {
            return false;
        }
        return match ($matchType) {
            'prefix' => str_starts_with($requestPath, rtrim($source, '/')),
            'wildcard' => (bool) preg_match('/^' . str_replace('\*', '.*', preg_quote($source, '/')) . '$/i', $requestPath),
            'regex' => @preg_match($source, $requestPath) === 1,
            default => rtrim($requestPath, '/') === rtrim($source, '/'),
        };
    }

    private function sendRedirect(string $target, int $code): void
    {
        $targetHost = strtolower((string) wp_parse_url($target, PHP_URL_HOST));
        $homeHost = strtolower((string) wp_parse_url(home_url('/'), PHP_URL_HOST));
        $allowedExternalHosts = [
            'help4network.com',
            'www.help4network.com',
            'md.help4wordpress.com',
            'help4wordpress.com',
            'www.help4wordpress.com',
            'plugins.help4wordpress.com',
        ];

        if ($targetHost === '' || $targetHost === $homeHost) {
            wp_safe_redirect($target, $code);
            return;
        }

        if (! in_array($targetHost, $allowedExternalHosts, true)) {
            return;
        }

        wp_redirect($target, $code); // phpcs:ignore WordPress.Security.SafeRedirect.wp_redirect_wp_redirect
    }

    private function requestIp(): string
    {
        return IpTools::currentIp();
    }

    private function redirectsTable(): string
    {
        global $wpdb;
        return $wpdb->prefix . 'h4_redirects';
    }

    private function notFoundTable(): string
    {
        global $wpdb;
        return $wpdb->prefix . 'h4_404_logs';
    }

}
