<?php

namespace WPSN\Modules\Redirects;

use WPSN\Core\AbstractModule;

defined('ABSPATH') || exit;

/**
 * Stage 7: Redirects + 404 monitor.
 */
final class RedirectsModule extends AbstractModule
{
    private RedirectRepository $redirects;

    private NotFoundLogRepository $notFound;

    private RedirectsSettingsRepository $settingsRepo;

    public function __construct(\WPSN\Core\Container $container)
    {
        parent::__construct($container);

        $cache = $container->get('cache');

        $this->redirects    = new RedirectRepository($cache, $this->logger);
        $this->notFound     = new NotFoundLogRepository($this->logger);
        $this->settingsRepo = new RedirectsSettingsRepository();
    }

    public function id(): string
    {
        return 'redirects';
    }

    public function name(): string
    {
        return __('Redirects & 404 Monitor', 'wp-seo-next');
    }

    public function description(): string
    {
        return __('Create 301/302/410 redirects and monitor 404s.', 'wp-seo-next');
    }

    public function defaultEnabled(): bool
    {
        return false;
    }

    public function register(): void
    {
        // Ensure tables exist. This is lightweight after first install.
        add_action('init', [RedirectsInstaller::class, 'maybeInstall'], 1);

        // Redirect engine.
        add_action('template_redirect', [$this, 'maybeRedirect'], 0);

        // Track post permalink changes and auto-create redirect rules when enabled.
        $autoPostRedirects = new AutoPostRedirectService($this->redirects, $this->settingsRepo, $this->logger);
        add_action('wp_after_insert_post', [$autoPostRedirects, 'handlePostUpdate'], 100, 4);

        // 404 logging.
        add_action('template_redirect', [$this, 'maybeLog404'], 99);

        // Cleanup hook (optional, used if scheduled).
        add_action('wpsn_redirects_cleanup_404', [$this, 'cleanup404Log']);

        // Admin UI.
        if (is_admin()) {
            $suggester = new RedirectSuggestionService($this->logger);
            $admin = new Admin\RedirectsAdmin(
                $this->redirects,
                $this->notFound,
                $this->settingsRepo,
                $suggester,
                $this->logger
            );
            $admin->register();

            // Redirect migration wizard (Tools → SEO Redirect Migration)
            $migrate = new Admin\RedirectsMigrationAdmin(
                $this->redirects,
                $this->logger
            );
            $migrate->register();
        }
    }

    public function boot(): void
    {
        // Schedule cleanup if needed.
        if (!wp_next_scheduled('wpsn_redirects_cleanup_404')) {
            wp_schedule_event(time() + 3600, 'daily', 'wpsn_redirects_cleanup_404');
        }
    }

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

        // Skip WP-CLI.
        if (defined('WP_CLI') && WP_CLI) {
            return;
        }

        $settings = $this->settingsRepo->getAll();

        if (!empty($settings['apply_only_on_404']) && !is_404()) {
            return;
        }

        $path = RedirectsUrl::currentPath();
        if ($path === '') {
            return;
        }

        $rule = $this->redirects->findMatch($path, !empty($settings['enable_regex']));
        if (!$rule) {
            return;
        }

        $status = (int) ($rule['status_code'] ?? 301);

        // Update hit counters (best effort).
        if (!empty($rule['id'])) {
            $this->redirects->incrementHit((int) $rule['id']);
        }

        if ($status === 410) {
            status_header(410);
            nocache_headers();
            echo esc_html__('Gone', 'wp-seo-next');
            exit;
        }

        if ($status === 451) {
            status_header(451);
            nocache_headers();
            echo esc_html__('Unavailable for Legal Reasons', 'wp-seo-next');
            exit;
        }

        $dest = RedirectsUrl::buildDestination(
            $rule,
            $path,
            !empty($settings['preserve_query'])
        );

        if ($dest === '') {
            return;
        }

        // Prevent loops.
        $current = RedirectsUrl::currentAbsoluteUrl();
        if ($current !== '' && RedirectsUrl::urlsEqual($current, $dest)) {
            return;
        }

        wp_redirect($dest, in_array($status, [301, 302, 307, 308], true) ? $status : 301);
        exit;
    }

    public function maybeLog404(): void
    {
        if (!is_404()) {
            return;
        }

        if (is_admin() || wp_doing_ajax() || (defined('REST_REQUEST') && REST_REQUEST)) {
            return;
        }

        $settings = $this->settingsRepo->getAll();
        if (empty($settings['log_404'])) {
            return;
        }

        if (!empty($settings['log_ignore_logged_in']) && is_user_logged_in()) {
            return;
        }

        $method = isset($_SERVER['REQUEST_METHOD']) ? strtoupper((string) $_SERVER['REQUEST_METHOD']) : 'GET';
        if (!in_array($method, ['GET', 'HEAD'], true)) {
            return;
        }

        $url = RedirectsUrl::currentRequestUri();
        if ($url === '') {
            return;
        }

        $ref = isset($_SERVER['HTTP_REFERER']) ? (string) $_SERVER['HTTP_REFERER'] : '';
        $ua  = isset($_SERVER['HTTP_USER_AGENT']) ? (string) $_SERVER['HTTP_USER_AGENT'] : '';

        $this->notFound->log($url, $ref, $ua);

        // Enforce limit immediately.
        $max = (int) ($settings['log_max_rows'] ?? 500);
        if ($max > 0) {
            $this->notFound->prune($max);
        }
    }

    public function cleanup404Log(): void
    {
        $settings = $this->settingsRepo->getAll();
        $max = (int) ($settings['log_max_rows'] ?? 500);
        if ($max > 0) {
            $this->notFound->prune($max);
        }
    }
}
